在FPGA开发过程中,我们会用到各种协议的接口。有的模块之间接口协议不同,则需要自行实现转接模块。近期我们某个工程需要将avalon memory mapped格式的接口转为apb格式的接口,以控制IP核的配置寄存器。使用system verilog语言可以编写简易模块实现。
首先简单介绍一下这两种接口协议。Avalon Memory-Mapped(Avalon-MM)协议是Altera公司在其 FPGA 产品中广泛采用的一种总线接口协议。它采用存储器映射方式,将所有外围设备映射到统一地址空间,主设备可通过地址对从设备进行读写操作。除clock和reset之外,基本的信号定义如下。
名称 | 方向 | 解释 |
---|---|---|
addr | i | 地址 |
read | i | 读信号 |
write | i | 写信号 |
readdata | o | 读出数据 |
readvalid | o | 读出数据有效 |
writedata | i | 写入数据 |
waitrequest | o | 当为1时,表明从设备繁忙,需要保持输入接口状态不变,直到waitrequest拉低 |
APB总线则是ARM公司推出的AMBA(Advanced Microcontroller Bus Architecture)总线架构中的一员,主打低功耗、低成本、简化接口,专为连接低速、低带宽的外围设备设计以 “简单稳定” 为核心优势。除clock和reset之外,基本信号如下。
名称 | 方向 | 解释 |
---|---|---|
sel | i | 从设备选择信号,表示正在选择该从设备 |
enable | i | 传输使能信号,表明在传输阶段 |
addr | i | 地址 |
write | i | 写标记,如果等于0则是读操作,否则是写操作。 |
writedata | i | 写入数据 |
readdata | o | 读出数据 |
ready | o | 当为1时,表示从设备准备就绪 |
APB总线从机接口具有3种状态,IDLE,SETUP和ACCESS,分别表示等待、传输建立和传输执行。采用enum结构进行实现,并将其作为状态机。
typedef enum logic [1:0] {
IDLE = 2'b00,
SETUP = 2'b01,
ACCESS = 2'b10
} state_t;
状态变迁则根据apb总线的定义执行,当收到avmm总线的read或write信号后,接下来两周期进入SETUP和ACCESS。随后根据后面从机是否ready,确定这一次传输结束或者继续等在这里。
always_comb begin
next_state = current_state;
case (current_state)
IDLE: begin
if (avmm_read || avmm_write) begin
next_state = SETUP;
end
end
SETUP: begin
next_state = ACCESS;
end
ACCESS: begin
if (apb_ready) begin
next_state = IDLE;
end
end
default: begin
next_state = IDLE;
end
endcase
end
如果是写数据过程,则需要在IDLE时刻,当avmm总线给出写数据请求时,锁存住需要写的地址和数据。如果是读数据过程,则需要在apb总线传输ACCESS时刻,将数据锁存住,等待返回给avmm。
always_ff @(posedge clk or posedge rst) begin
if (rst) begin
apb_addr_reg <= '0;
apb_write_reg <= 1'b0;
apb_wdata_reg <= '0;
end else if (current_state == IDLE && (avmm_read || avmm_write)) begin
// when access is detected in IDLE state, latch address and data
apb_addr_reg <= {{(APB_ADDR_WIDTH-AVMM_ADDR_WIDTH){1'b0}}, avmm_addr};
apb_write_reg <= avmm_write;
apb_wdata_reg <= avmm_wdata;
end
end
always_ff @(posedge clk or posedge rst) begin
if (rst) begin
avmm_rdata_reg <= '0;
avmm_rdata_valid_reg <= 1'b0;
end else if (current_state == ACCESS && apb_ready && !apb_write_reg) begin
// when APB read operation is completed, latch read data
avmm_rdata_reg <= apb_rdata;
avmm_rdata_valid_reg <= 1'b1;
end else begin
avmm_rdata_valid_reg <= 1'b0;
end
end
最后,需要返回给avmm总线的waitrequest信号。我们规定,只有模块处于IDLE状态,才允许avmm总线信号变化,否则需要保持不变。因此直接使用以下语句即可。
assign avmm_waitrequest = (current_state != IDLE);
最终实现的读写波形分别如下。