ATA,
//------------ APB ------------
//input
input [31:0] iPRDATA,
//output
output oPSEL0,
output oPSEL1,
output oPWRITE,
output oPENABLE,
output [31:0] oPADDR,
output [31:0] oPWDATA
);
/*————————————————————————————————————————————————————————————————————————*\
/ AHB Signal Register \
\*————————————————————————————————————————————————————————————————————————*/
reg iHWRITE_r ;
reg [31:0] iHADDR_r ;
reg [31:0] iHWDATA_r ;
always@( posedge iHCLK) begin
if(!iHRESETn) begin
iHWRITE_r <= 1'b0;
iHADDR_r <= 32'b0;
iHWDATA_r <= 32'b0;
end
else if( iHSEL && (bridge_state == BRIDGE_IDLE || bridge_state == BRIDGE_ENABLE))begin
iHWRITE_r <= iHWRITE; // ahb reg change when bridge_state is going to change:
iHADDR_r <= iHADDR ; // from IDLE to SETUP
iHWDATA_r <= iHWDATA; // from ENABLE to SETUP
end
end
/*————————————————————————————————————————————————————————————————————————*\
/ Bridge FSM \
\*————————————————————————————————————————————————————————————————————————*/
reg [1:0] bridge_state;
always @(posedge iHCLK ) begin
if (!iHRESETn) begin
bridge_state <= BRIDGE_IDLE;
end else begin
case ( bridge_state )
// IDLE
BRIDGE_IDLE: begin
if(iHSEL) begin
bridge_state <= BRIDGE_SETUP;
end
else begin
bridge_state <= BRIDGE_IDLE;
end
end
// SETUP
BRIDGE_SETUP: begin
bridge_state <= BRIDGE_ENABLE;
end
// ENABLE
BRIDGE_ENABLE: begin
if(iHSEL) begin
bridge_state <= BRIDGE_SETUP;
end
else begin
bridge_state <= BRIDGE_IDLE;
end
end
//DEFAULT
default: bridge_state <= BRIDGE_IDLE;
endcase
end
end
/*————————————————————————————————————————————————————————————————————————*\
/ AHB Slave Output \
\*————————————————————————————————————————————————————————————————————————*/
assign oHREADY = ( bridge_state == BRIDGE_SETUP ) ? 1'b0 : 1'b1;
assign oHRESP = OKAY ;
assign oHRDATA = iPRDATA;
/*————————————————————————————————————————————————————————————————————————*\
/ APB Master Output \
\*————————————————————————————————————————————————————————————————————————*/
assign oPSEL0 = ( iHADDR_r == ADDR_GPIO_0) ? 1'b1 : 1'b0;
assign oPSEL1 = ( iHADDR_r == ADDR_GPIO_1) ? 1'b1 : 1'b0;
assign oPWRITE = iHWRITE_r ;
assign oPENABLE = ( bridge_state == BRIDGE_ENABLE ) ? 1'b1 : 1'b0;
assign oPADDR = iHADDR_r;
assign oPWDATA = iHWDATA_r;
endmodule
3. GPIO模块设计
GPIO(General Purpose I/O),是一种用于对器件的引脚做观测或控制的外设,
在STM32、ZYNQ等开发中经常被使用到,相信大家并不陌生,
而在本项目中,我们将尝试自己用Verilog语言,设计一个具有基本功能的GPIO外设,
该模块具有一个APB Slave接口,
GPIO模块会被挂在APB总线上,实现流水灯控制系统和开发板的外设(LED灯&按键)的交互,
模块的信号定义如下表:
其中oGPIOout[3]连接开发板上LED4引脚,oGPIOout[2]连接LED3引脚,oGPIOout[1]连接LED2引脚,oGPIOout[0]连接LED1引脚,
iGPIOin[3:0]也是按照该顺序,依次连接开发板上的4个按键:KEY4,KEY3,KEY2,KEY1。
3.1 GPIO模块寄存器
GPIO模块的功能实现,主要依靠四个32位寄存器:1.DATA_RO寄存器 2.DATA寄存器 3. DIRM寄存器 4. OEN寄存器
主机通过配置/读写这些寄存器,就可以实现对外设的操作,
下面分析这四个寄存器:
-
DATA_RO:
用来观测GPIO引脚状态,若引脚被配置成输出模式,则该寄存器会反映驱动该引脚的电平的状态。
DATA_RO是一个只读寄存器,对该寄存器的写操作是无效的
-
DATA:
当GPIO某一引脚被配置为输出模式时,用来控制该引脚的输出状态
-
DIRM:
用来配置GPIO各个引脚的方向(做输入or做输出),
当DIRMP[x]==0,第x位引脚为输入引脚,其输出功能被disable
-
OEN:
当GPIO某一引脚被配置为输出模式时,用来使能该引脚的输出功能,
当OEN[x]==0时,第x位引脚的输出功能被disable
为了避免在设计中引入双向端口,我们为GPIO模块赋予了一个32位输入端口iGPIOin[31:0],一个32位输出端口oGPIOout[31:0],
当reg_DIRM[i]=0,
对应i号引脚被DIRM寄存器配置为输入端口,
oGPIOout[i]呈现高阻态1'bz,该位对应的引脚输出功能实际上是不存在的,
iGPIOin[i]