gt; DATA_RO -> PRADATA
assign oPRDATA = reg_DATA_RO;
// reg_DATA -> GPIOout
reg [31:0] oGPIOout ;
always @(*) begin
for ( i=0 ; i<32 ; i=i+1 ) begin
if( reg_DIRM[i] & reg_OEN[i] ) begin //output mode
oGPIOout[i] = reg_DATA[i] ;
end else begin
oGPIOout[i] = 1'bz;
end
end
end
endmodule
4. Control Unit(流水灯控制单元)模块设计
4.1 有限状态机设计
我们的AMBA流水灯控制系统的核心:Control Unit,即控制单元,
Bridge模块是APB上所有外设的Master,而CU模块则是Bridge在AHB上的Master,
可以说是Master中的Master了,
Control Unit是信号传输的发起者,决定总线上是在读还是在写、也决定访问的从机是哪个,
CU发出的控制信号包括HTRANS、HBURST、HSIZE、HADDR,数据信号包括HWDATA,
这些AHB信号如何生成均是由Control Unit负责的,
同时CU也需要接收HRDATA,并对其进行解读判断,
那么这些AHB信号是如何生成的呢?
实际上,Control Unit模块也是通过一个有限状态机实现的:
下面介绍一下该FSM的各个状态所代表的含义:
1. CONFIG_0~2:按下复位按钮/上电后,对GPIO外设进行初始化配置,
其中,CONFIG_0:
将GPIO的0~3位引脚配置为输入模式,连接FPGA开发板的Key按键进行观测
将GPIO的4~7位引脚配置为输出模式,连接FPGA开发板的LED灯进行驱动控制
CONFIG_1:
将GPIO的4~7位引脚的输出电平均设定为高电平1(本开发板上的LED为共阳极,引脚高电平=LED灯熄灭)
CONFIG_2:
对GPIO的4~7位引脚进行输出使能
2. READ_DATA_RO:
读GPIO的DATA_RO寄存器,根据寄存器[3:0]的值得知Key状态,
该状态是FSM的核心状态,
当配置完GPIO的DIRM、OEN、DATA寄存器后,
我们会开始反复进行读DATA_RO,即观测KEY的状态,
然后根据KEY的值得到对应的LED_mode,
随后会由该状态跳向WRITE_DATA_0~3,对DATA寄存器进行配置,控制LED灯点亮or熄灭
3. WRITE_DATA_0~3:
配置不同的流水灯灯工作模式,模式由KEY状态决定,
按下KEY1进入工作模式0,按下KEY2进入工作模式1,按下KEY3进入工作模式2,按下KEY4进入工作模式3,
在初始静止状态下,只有同时按下KEY0~3才能进入工作模式0,
此时按下某个单独的KEY不会有任何反应的
随着FSM状态的改变,CU模块将会给AHB总线发出不同的读写命令,从而实现对GPIO模块的寄存器配置,
下表具体地整理了FSM状态和CU模块生成的AHB信号之间的对应关系:
上述FSM状态转换逻辑所对应的RTL:
reg [2:0] CU_state;
always @(posedge iHCLK ) begin
if ( !iHRESETn ) begin
CU_state <= CONFIG_0;
end else begin
case (CU_state)
// write GPIO_DIRM
CONFIG_0: begin
if ( iHREADY ) begin
CU_state <= CONFIG_1;
end else begin
CU_state <= CONFIG_0;
end
end
// write GPIO_DATA
CONFIG_1: begin
if ( iHREADY ) begin
CU_state <= CONFIG_2;
end else begin
CU_state <= CONFIG_1;
end
end
// write GPIO_OEN
CONFIG_2: begin
if ( iHREADY && iHRDATA[3:0] == 4'b0000) begin
CU_state <= READ_DATA_RO;
end else begin
CU_state <= CONFIG_2;
end
end
// read DATA_RO
READ_DATA_RO: begin
if ( iHREADY && iHRDATA[3:0] == 4'b1110) begin //key1 pressed
CU_state <= WRITE_DATA_0;
end else if ( iHREADY && iHRDATA[3:0] == 4'b1101) begin //key2 pressed
CU_state <= WRITE_DATA_1;
end else if ( iHREADY && iHRDATA[3:0] == 4'b1011) begin //key3 pressed
CU_state <= WRITE_DATA_2;
end else if ( iHREADY && iHRDATA[3:0] == 4'b0111) begin //key4 pressed
CU_state <= WRITE_DATA_3;
end else if ( iHREADY && led_mode[0] ) begin //keep mode0 if not pressed this moment
CU_state <= WRITE_DATA_0;
end else if ( iHREADY && led_mode[1] ) begin //keep mode1
CU_state <= WRITE_DATA_0;
end else if ( iHREADY && led_mode[2] ) begin //keep mode2
CU_state <= WRITE_DATA_1;
end else if ( iHREADY && led_mode[3] ) begin //keep mode3
CU_state <= WRITE_DATA_2;
end else begin
CU_state <= READ_DATA_RO; // Slave not ready || no key ever been pressed
end
end
// write DATA
WRITE_DATA_0 : begin
if ( iHREADY ) begin
CU_state <= READ_DATA_RO;
end else begin
CU_state <= CU_state;
end
end
// write DATA
WRITE_DATA_1 : begin
if ( iHREADY ) begin
CU_state <= READ_DATA_RO;
end else begin
CU_state <= CU_state;
end
end
// write DATA
WRITE_DATA_2 : begin
if ( iHREADY ) begin
CU_state <= READ_DATA_RO;
end else begin
CU_state <