APB Protocol - Master, Slave, and Top Module
INTERNAL ARCHITECTURE
TIMING DIAGRAMS
WRITE TRANSFER: with wait states
READ TRANSFER: without wait states
READ TRANSFER: with wait states
APB Master Module
module apb_master (
input PCLK,
input PRESETn,
output reg PSEL,
output reg PENABLE,
output reg PWRITE,
output reg [31:0] PADDR,
output reg [31:0] PWDATA,
input [31:0] PRDATA,
input PREADY
);
reg [3:0] state;
parameter IDLE=0, WRITE_SETUP=1, WRITE_ACCESS=2, READ_SETUP=3, READ_ACCESS=4;
always @(posedge PCLK or negedge PRESETn) begin
if (!PRESETn) begin
state <= IDLE;
PSEL <= 0; PENABLE <= 0; PWRITE <= 0;
PADDR <= 0; PWDATA <= 0;
end else begin
case (state)
IDLE: begin
PSEL <= 1;
PWRITE <= 1;
PADDR <= 32'h00000004;
PWDATA <= 32'hABCD1234;
state <= WRITE_SETUP;
end
WRITE_SETUP: begin
PENABLE <= 1;
state <= WRITE_ACCESS;
end
WRITE_ACCESS: begin
if (PREADY) begin
PSEL <= 1;
PWRITE <= 0;
PADDR <= 32'h00000004;
PENABLE <= 0;
state <= READ_SETUP;
end
end
READ_SETUP: begin
PENABLE <= 1;
state <= READ_ACCESS;
end
READ_ACCESS: begin
if (PREADY) begin
$display("READ DATA @%0t: %h", $time, PRDATA);
state <= IDLE;
PSEL <= 0; PENABLE <= 0;
end
end
endcase
end
end
endmodule
APB Slave Module
module apb_slave (
input PCLK,
input PRESETn,
input [31:0] PADDR,
input PSEL,
input PENABLE,
input PWRITE,
input [31:0] PWDATA,
output reg PREADY,
output reg PSLVERR,
output reg [31:0] PRDATA
);
reg [31:0] mem [0:31];
parameter [1:0] IDLE = 2'b00, SETUP = 2'b01, ACCESS = 2'b10;
reg [1:0] state;
always @(posedge PCLK or negedge PRESETn) begin
if (!PRESETn) begin
state <= IDLE;
PREADY <= 0;
PRDATA <= 0;
PSLVERR <= 0;
end else begin
case (state)
IDLE: begin
PREADY <= 0;
if (PSEL && !PENABLE)
state <= SETUP;
end
SETUP: begin
PREADY <= 0;
if (PSEL && PENABLE)
state <= ACCESS;
end
ACCESS: begin
PREADY <= 1;
PSLVERR <= 0;
if (PADDR[4:0] < 32) begin
if (PWRITE)
mem[PADDR[4:0]] <= PWDATA;
else
PRDATA <= mem[PADDR[4:0]];
end else begin
PSLVERR <= 1;
end
if (!PSEL)
state <= IDLE;
end
default: state <= IDLE;
endcase
end
end
endmodule
APB Top Module
module apb_top;
reg PCLK;
reg PRESETn;
wire PSEL;
wire PENABLE;
wire PWRITE;
wire [31:0] PADDR;
wire [31:0] PWDATA;
wire [31:0] PRDATA;
wire PREADY;
wire PSLVERR;
initial begin
PCLK = 0;
forever #5 PCLK = ~PCLK;
end
initial begin
PRESETn = 0;
#12 PRESETn = 1;
end
apb_master master_inst (
.PCLK(PCLK),
.PRESETn(PRESETn),
.PSEL(PSEL),
.PENABLE(PENABLE),
.PWRITE(PWRITE),
.PADDR(PADDR),
.PWDATA(PWDATA),
.PRDATA(PRDATA),
.PREADY(PREADY)
);
apb_slave slave_inst (
.PCLK(PCLK),
.PRESETn(PRESETn),
.PADDR(PADDR),
.PSEL(PSEL),
.PENABLE(PENABLE),
.PWRITE(PWRITE),
.PWDATA(PWDATA),
.PRDATA(PRDATA),
.PREADY(PREADY),
.PSLVERR(PSLVERR)
);
initial begin
$dumpfile("apb.vcd");
$dumpvars(0, apb_top);
#200 $finish;
end
endmodule