[하만]세미콘 아카데미/verilog
0617 AXI_memory
내머리속은이런걸로
2024. 7. 10. 12:02
Overlapping read bursts
: 읽기 작업을 동시에 수행하여 데이터 전송 속도 향상시킨다.
- 마스터가 첫 번째 읽기 요청으로 주소 ARADDR를 슬레이브로 전송한다.
- 슬레이브가 RDATA를 마스터에게 보낸다.
- 첫 번째 데이터가 전송되는 동안, 마스터가 두 번째로 읽을 주소 ARDDR을 슬레이브로 전송한다.
- 슬레이브가 첫 번재 데이터 전송이 완료되면 두 번째 데이터를 전송한다.
- 과정이 반복된다.
Write Burst
: Master가 여러 데이터 단위를 Slave로 연속적으로 전송할 때 사용한다.
- 주소 설정 (Address Phase) : AWADDR, AWVALID / AWREADY
- 데이터 전송 (Data Phase) : WDATA, WVALID / WREADY, WLAST
- 응답 전송 (Response Phase) : BREADY / BRESP, BVALID
(Master / Slave)
- BREADY : Master가 응답을 받을 준비가 되었음을 나타낸다.
- BRESP : Slave가 쓰기 요청에 대한 응답 상태를 설정한다. (정상/에러)
- BVALID : 응답이 유효함을 나타내기 위해 이 신호를 1로 설정한다.
AWID
: Master가 여러 쓰기 요청을 동시에 발생할 때 요청을 구별할 수 있도록 한다.
AMBA BUS Lite
READY : VALID와 IFO가 ON되면 ON
MASTER에서 VALID를 ON하고, Write할 주소를 보내면 Slave에서 READY 신호를 보낸다.
- WLAST는 AXI4-Lite에서는 사용하지 않는다.
Slave를 Memory라고 생각하여 AWADDR, AWVALID 신호를 보낸다.
: 쓰기 데이터가 유효한 바이트를 식벼랗여 특정 바이트만 수정할 수 있게 한다.
- strobe : WSTRB
- Master → Slave Data byte 정보
- 32bit의 경우 4bit
- 64bit의 경우 8bit
ex)
- WDATA : 0x11223344
- WSTRB : 1100
- WSTRB[3] → 상위 바이트(11)은 유효하다.
- WSTRB[2] → 두 번째 바이트(22)은 유효하다.
- WSTRB[1] → 세 번째 바이트(33)은 유효하다.
- WSTRB[0] → 하위 바이트(44)은 유효하다.
코드 구현 주의사항
Master에서 Slave로 AWADDR, AWVALID를 전송, triggering을 하는 시점은 정해져있지 않는다.
→ 개발자하기 나름이다.
- Address가 0 이상이면 triggering
- VALID = 1이면 triggering
- READY = 1이면 transaction을 종료한다.
→ Address가 0인 것은 사용하지 못한다는 단점이 존재한다.
AXI_Memory.sv
`timescale 1ns / 1ps
module AXI_Memory ();
endmodule
module AXI_Master (
// Global Signal
input logic ACLK,
input logic ARESETn, // n : negetive active
// AW Channel
output logic [31:0] AWADDR,
output logic AWVALID,
input logic AWREADY,
// AR Channel
output logic [31:0] ARADDR,
output logic ARVALID,
input logic ARREADY,
// W Channel
output logic [31:0] WDATA,
output logic WVALID,
output logic [ 3:0] WSTRB,
input logic WREADY,
// R Channel
input logic [31:0] RDATA,
input logic RVALID,
output logic RREADY,
// B Channel
input logic [ 1:0] BRESP,
input logic BVALID,
output logic BREADY,
// user signal write transaction
input logic [31:0] aw_addr,
input logic [31:0] w_data,
input logic valid,
input logic [ 3:0] w_strb,
output logic ready,
// user signal read transaction
input logic [31:0] raddr,
input logic rvalid,
output logic [31:0] rdata,
output logic rready
);
// AW Channel
enum bit {
AW_IDLE_S,
AW_VALID_S
}
aw_state, aw_state_next;
logic [31:0] aw_addr_reg, aw_addr_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 // triggering time decision
aw_state_next = AW_VALID_S;
aw_addr_next = aw_addr; // address latch
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
// AR Channel
enum bit {
AR_IDLE_S,
AR_VALID_S
}
ar_state, ar_state_next;
logic [31:0] ar_addr_reg, ar_addr_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 (rvalid) begin // triggering time decision
ar_state_next = AR_VALID_S;
ar_addr_next = raddr; // address latch
end
end
AR_VALID_S: begin
ARVALID = 1'b1;
ARADDR = ar_addr_reg;
if (ARVALID && ARREADY) begin
ar_state_next = AR_IDLE_S;
end
end
endcase
end
// W Channel
enum bit {
W_IDLE_S,
W_VALID_S
}
w_state, w_state_next;
logic [31:0] w_data_reg, w_data_next;
logic [3:0] w_strb_reg, w_strb_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
w_state_next = W_VALID_S;
w_data_next = w_data; // data latch
w_strb_next = w_strb; // strobe latch
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
// R Channel
enum bit {
R_IDLE_S,
R_READY_S
}
r_state, r_state_next;
logic [31:0] r_data_reg, r_data_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;
RREADY = 1'b0;
rready = 1'b0;
case (r_state)
R_IDLE_S: begin
RREADY = 1'b0;
rready = 1'b0;
if (ARVALID) begin
r_state_next = R_READY_S;
end
end
R_READY_S: begin
RREADY = 1'b1;
if (RVALID && RREADY) begin
r_state_next = R_IDLE_S;
rdata = RDATA;
rready = 1'b1;
end
end
endcase
end
// B Channel
enum bit {
B_IDLE_S,
B_READY_S
}
b_state, b_state_next;
logic [31:0] b_resp_reg, b_resp_next; // response
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;
case (b_state)
B_IDLE_S: begin
BREADY = 1'b0;
ready = 1'b0;
if (WVALID) begin
b_state_next = B_READY_S;
end
end
B_READY_S: begin
BREADY = 1'b1;
if (BVALID && BREADY) begin
b_state_next = B_IDLE_S;
b_resp_next = BRESP;
ready = 1'b1;
end
end
endcase
end
endmodule
module AXI_Slave_Memory (
// Global Signal
input logic ACLK,
input logic ARESETn,
// AW Channel (Write Address)
input logic [31:0] AWADDR,
input logic AWVALID,
output logic AWREADY,
// AR Channel
input logic [31:0] ARADDR,
input logic ARVALID,
output logic ARREADY,
// W Channel
input logic [31:0] WDATA,
input logic WVALID,
input logic [ 3:0] WSTRB,
output logic WREADY,
// R Channel
output logic [31:0] RDATA,
output logic RVALID,
input logic RREADY,
// B Channel
output logic [ 1:0] BRESP,
output logic BVALID,
input logic BREADY,
output logic ready
);
logic [7:0] slave_mem[0:15];
// AW Channel
enum bit {
AW_IDLE_S,
AW_READY_S
}
aw_state, aw_state_next;
logic [31:0] aw_addr_reg, aw_addr_next; // latch data
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
aw_state_next = AW_READY_S;
aw_addr_next = AWADDR; // address latch
end
end
AW_READY_S: begin
AWREADY = 1'b1;
aw_addr_next = AWADDR;
if (AWVALID && AWREADY) begin
aw_state_next = AW_IDLE_S;
end
end
endcase
end
// AR Channel
enum bit {
AR_IDLE_S,
AR_READY_S
}
ar_state, ar_state_next;
logic [31:0] ar_addr_reg, ar_addr_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;
ARREADY = 1'b0;
case (ar_state)
AR_IDLE_S: begin
ARREADY = 1'b0;
if (ARVALID) begin // triggering time decision
ar_state_next = AR_READY_S;
ar_addr_next = ARADDR; // address latch
end
end
AR_READY_S: begin
ARREADY = 1'b1;
ar_addr_next = ARADDR;
if (ARVALID && ARREADY) begin
ar_state_next = AR_IDLE_S;
end
end
endcase
end
// W Channel
enum bit {
W_IDLE_S,
W_READY_S
}
w_state, w_state_next;
logic [31:0] w_data_reg, w_data_next;
logic [3:0] w_strb_reg, w_strb_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 // get address && wdata in
w_state_next = W_READY_S;
w_data_next = WDATA; // data latch
w_strb_next = WSTRB; // strobe latch
end
end
W_READY_S: begin
WREADY = 1'b1;
//slave_mem[aw_addr_reg] = w_data_reg; // write
case (w_strb_reg)
4'b0001: begin
slave_mem[aw_addr_reg] = w_data_reg[7:0];
end
4'b0010: begin
slave_mem[aw_addr_reg+1] = w_data_reg[15:8];
end
4'b0100: begin
slave_mem[aw_addr_reg+2] = w_data_reg[23:16];
end
4'b1000: begin
slave_mem[aw_addr_reg+3] = w_data_reg[31:24];
end
4'b0011: begin
slave_mem[aw_addr_reg] = w_data_reg[7:0];
slave_mem[aw_addr_reg+1] = w_data_reg[15:8];
end
4'b0101: begin
slave_mem[aw_addr_reg] = w_data_reg[7:0];
slave_mem[aw_addr_reg+2] = w_data_reg[23:16];
end
4'b1001: begin
slave_mem[aw_addr_reg] = w_data_reg[7:0];
slave_mem[aw_addr_reg+3] = w_data_reg[31:24];
end
4'b0110: begin
slave_mem[aw_addr_reg+1] = w_data_reg[15:8];
slave_mem[aw_addr_reg+2] = w_data_reg[23:16];
end
4'b1010: begin
slave_mem[aw_addr_reg+1] = w_data_reg[15:8];
slave_mem[aw_addr_reg+3] = w_data_reg[31:24];
end
4'b1100: begin
slave_mem[aw_addr_reg+2] = w_data_reg[23:16];
slave_mem[aw_addr_reg+3] = w_data_reg[31:24];
end
4'b0111: begin
slave_mem[aw_addr_reg] = w_data_reg[7:0];
slave_mem[aw_addr_reg+1] = w_data_reg[15:8];
slave_mem[aw_addr_reg+2] = w_data_reg[23:16];
end
4'b1011: begin
slave_mem[aw_addr_reg] = w_data_reg[7:0];
slave_mem[aw_addr_reg+1] = w_data_reg[15:8];
slave_mem[aw_addr_reg+3] = w_data_reg[31:24];
end
4'b1101: begin
slave_mem[aw_addr_reg] = w_data_reg[7:0];
slave_mem[aw_addr_reg+2] = w_data_reg[23:16];
slave_mem[aw_addr_reg+3] = w_data_reg[31:24];
end
4'b1110: begin
slave_mem[aw_addr_reg+1] = w_data_reg[15:8];
slave_mem[aw_addr_reg+2] = w_data_reg[23:16];
slave_mem[aw_addr_reg+3] = w_data_reg[31:24];
end
4'b1111: begin
slave_mem[aw_addr_reg] = w_data_reg[7:0];
slave_mem[aw_addr_reg+1] = w_data_reg[15:8];
slave_mem[aw_addr_reg+2] = w_data_reg[23:16];
slave_mem[aw_addr_reg+3] = w_data_reg[31:24];
end
default: begin
end
endcase
if (WVALID && WREADY) begin
w_state_next = W_IDLE_S;
end
end
endcase
end
// R Channel
enum bit {
R_IDLE_S,
R_VALID_S
}
r_state, r_state_next;
logic [31:0] r_data_reg, r_data_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;
RVALID = 1'b0;
case (r_state)
R_IDLE_S: begin
RVALID = 1'b0;
if (ARREADY && ARVALID) begin
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];
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
// B Channel
logic [31:0] b_resp_reg, b_resp_next; // response
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;
ready = 1'b0;
case (b_state)
B_IDLE_S: begin
BVALID = 1'b0;
if (WVALID && WREADY) begin // WREADY : Write done
b_state_next = B_VALID_S;
b_resp_next = 2'b00; // OKAY Response
end
end
B_VALID_S: begin
BVALID = 1'b1;
BRESP = b_resp_reg;
if (BVALID && BREADY) begin
b_state_next = B_IDLE_S;
b_resp_next = BRESP;
ready = 1'b1;
end
end
endcase
end
endmodule
tb_AXI_Memory .sv
`timescale 1ns / 1ps
module tb_AXI_Memory ();
// Global Signal
logic ACLK;
logic ARESETn;
// AW Channel (Write Address)
logic [31:0] AWADDR;
logic AWVALID;
logic AWREADY;
// AR Channel
logic [31:0] ARADDR;
logic ARVALID;
logic ARREADY;
// W Channel
logic [31:0] WDATA;
logic WVALID;
logic [ 3:0] WSTRB;
logic WREADY;
// R Channel
logic [31:0] RDATA;
logic RVALID;
logic RREADY;
// B Channel
logic [ 1:0] BRESP;
logic BVALID;
logic BREADY;
// user signal write transaction
logic [31:0] aw_addr;
logic [31:0] w_data;
logic valid;
logic [ 3:0] w_strb;
logic ready;
// user signal read transaction
logic [31:0] raddr;
logic rvalid;
logic [31:0] rdata;
logic rready;
always #5 ACLK = ~ACLK;
AXI_Master dut_master (.*);
AXI_Slave_Memory dut_slave (.*);
initial begin
ACLK = 1'b0;
ARESETn = 1'b0;
#20 ARESETn = 1'b1;
// write
// 1 transaction
@(posedge ACLK);
aw_addr = 32'd0;
w_data = 32'h000000ff;
w_strb = 4'b0001;
valid = 1'b1;
@(posedge ACLK);
valid = 1'b0;
@(posedge ready);
#20 $finish;
// 1 transaction
@(posedge ACLK);
aw_addr = 32'd1;
w_data = 32'h0000aabb;
w_strb = 4'b0011;
valid = 1'b1;
@(posedge ACLK);
valid = 1'b0;
@(posedge ready);
#20 $finish;
// 1 transaction
@(posedge ACLK);
aw_addr = 32'd3;
w_data = 32'h00aabbcc;
w_strb = 4'b0111;
valid = 1'b1;
@(posedge ACLK);
valid = 1'b0;
@(posedge ready);
// 1 transaction
@(posedge ACLK);
aw_addr = 32'd6;
w_data = 32'haabbccdd;
w_strb = 4'b1111;
valid = 1'b1;
@(posedge ACLK);
valid = 1'b0;
@(posedge ready);
// read
// 1 transaction
@(posedge ACLK);
raddr = 32'd0;
rvalid = 1'b1;
@(posedge ACLK);
rvalid = 1'b0;
@(posedge rready);
// 1 transaction
@(posedge ACLK);
raddr = 32'd1;
rvalid = 1'b1;
@(posedge ACLK);
rvalid = 1'b0;
@(posedge rready);
#20 $finish;
end
endmodule
simulation