APB3是一个低功耗低成本接口。所有信号在时钟上升沿传输,每次传输需要两个时钟周期。
1、Interface
信号 | 控制端 | 描述 |
PSEL | M | 选通。APB master会将此信号生成给每个slave。它指示已选择的slave,并且需要进行数据传输。每个slave都有一个PSEL信号。 |
PADDR | M | 地址总线,最大位宽32位 |
PENABLE | M | 使能。当它为高时,表示读写过程有效 |
PWRITE | M | 读写控制。为高时表示写操作,为低时表示读操作 |
PWDATA | M | 写数据。master通过PWDATA将数据写到slave,该总线最大宽度为32位 |
PRDATA | S | 读数据。master通过PRDATA将数据从slave读取回来,该总线最大宽度为32位 |
PREADY | S | 在PSEL和PENABLE为高时,总线会查看PREADY是否为高,如果为高则数据有效,如果为低则等待其变高。 |
APB写过程
没有等待状态。
(1)T0到T1阶段是空闲状态,
(2)T1到T2是setup阶段,此阶段会准备好PADDR,PWRITE(为1),PWDATA。
(3)T2到T3是Access阶段,此阶段PENABLE会拉高,并且地址、数据和控制信号仍然保持有效。
(4)T3到T4阶段PENABLE再次拉低;选择信号PSELx也会拉低,除非紧跟同一外设下一次的传输。
有等待状态
在ACESS阶段,当PENABLE为高时,可以通过PREADY拉低来延长ACESS阶段。这时要保持PADDR,PWRITE,PSEL,PENABLE,PWDATA信号保持不变。
当PENABLE为低时,PREADY可以高也可以低。所以如果外设是固定两个操作周期时,PREADY可以固定为高。
另外推荐地址和写信号只在下一个访问周期才发生变化,这样可以节省功耗 。
APB读过程
读操作
在SETUP阶段读过程与写过程是一样的,只是写过程PWRITE为高,读过程PWRITE为低。
同样在ACCESS阶段,也可以通过拉低PREADY信号延长ACESS阶段,但是要保证PADDR,PWRITE,PSEL和PENALBE为固定状态。
通过RISCV 操作APB3也比较简单,如下:
slave是指APB的基地址,addr是指APB的偏移地址,也就是PADDR.
void apb3_write(u32 slave, u32 addr, u32 data ) { write_u32(data,slave+addr); } void abp3_read(u32 slave, u32 addr) { return read_u32(slave+addr); }
在逻辑上处理也比较简单,易灵思提供了简单的APB3参考。
//以下为易灵思提供的APB3的参考 module apb3_slave_memory #( // user parameter starts here // parameter ADDR_WIDTH = 16, parameter DATA_WIDTH = 32, parameter NUM_REG = 4 ) ( // user logic starts here input clk, input resetn, input [ADDR_WIDTH-1:0] PADDR, input PSEL, input PENABLE, output PREADY, input PWRITE, input [DATA_WIDTH-1:0] PWDATA, output [DATA_WIDTH-1:0] PRDATA, output PSLVERROR ); ///////////////////////////////////////////////////////////////// localparam [1:0] IDLE = 2'b00, SETUP = 2'b01, ACCESS = 2'b10; reg [1:0] busState, busNext; reg slaveReady; wire actWrite, actRead; ////////////////////////////////////////////////////////////////// always@(posedge clk or negedge resetn) begin if(!resetn) busState <= IDLE; else busState <= busNext; end always@(*) begin busNext = busState; case(busState) IDLE: begin if(PSEL && !PENABLE) busNext = SETUP; else busNext = IDLE; end SETUP: begin if(PSEL && PENABLE) busNext = ACCESS; else busNext = IDLE; end ACCESS: begin if(PREADY) busNext = IDLE; else busNext = ACCESS; end default: begin busNext = IDLE; end endcase end assign actWrite = PWRITE && (busState == ACCESS); assign actRead = !PWRITE && (busState == ACCESS); assign PSLVERROR = 1'b0; assign PREADY = slaveReady & & (busState !== IDLE); always@ (posedge clk) begin slaveReady <= actWrite | actRead; end simple_dual_port_ram #( .DATA_WIDTH(32), .ADDR_WIDTH(16), .OUTPUT_REG(0), .RAM_INIT_FILE("") ) dut ( .wdata (PWDATA ), .waddr (PADDR ), .wclk (clk ), .we (actWrite ), .raddr (PADDR ), .rclk (clk ), .re (actRead ), .rdata (PRDATA ) ); endmodule
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !