目录
一、实验目的
二、实验工具及环境
三、实验内容及步骤
1.实验3.1 主机八位增量突发传输写入RAM
(1)设计思路
(2)状态机实现
2.实验3.2 主机FIFO十六位增量突发传输写入RAM
(1)设计思路
(2)状态机实现
四、实验结论及分析
1.实验3.1 主机八位增量突发的验证
2.实验3.2 主机十六位增量突发的验证
【附录】
AHB_Master.v
AHB_Masrer_FIFO.v
学习并掌握基本的AHB总线传输协议;使用Verilog HDL语言对AHB主机驱动模块进行设计,并满足正常的时序要求,使主机发起八位增量突发和十六位增量突发;掌握Modelsim仿真工具的使用。
1.Windows 10;
2.Modelsim 10.5;
3.Vivado 20.1;
4.Visual Studio Code。
本实验需要设计一个AHB主机接口模块,提供主机到AHB总线的接口,其框图如下图所示:
同实验1,为了简化设计,不需要全面考虑所有可能的接口配置情况,只需要考虑HSIZE=3’b010即八位(一个字节)传输、HPROT=4’b0001即数据/预取指、HRESP为OKAY即传输成功的情况。
采用三段式状态机来实现主机八位增量传输,其状态转移图如下图所示。
①状态定义以及产生下一个状态的组合逻辑
主机AHB_Master采用了三个状态:空闲IDLE、请求REQ、发送SEND来完成一次八位增量突发。当AHB_Master处于IDLE空闲状态时,如果使能信号en有效则进入REQ向仲裁器请求发起传输,否则留在IDLE状态;当AHB_Master处于REQ请求状态时,当HGRANT授权信号有效,则进入发送状态,若HGRANT无效或者en信号失效的话分别进入REQ或者IDLE状态;当AHB_Master处于SEND状态时,如果send_cnt计数器为111即记满0~7一共八个周期的时候进入IDLE状态,否则停留在SEND状态继续发送数据。状态编码和状态转移组合逻辑代码如下所示:
②数据寄存器
在一次传输的过程中,在上一个周期地址总线HADRR发送地址(地址相位),在下一个周期数据总线HRDATA或HWDATA接收或发送数据(数据周期)。而主机的数据和地址是同步给出的,因此就需要打一拍使得数据的发送慢于地址的发送一个周期,代码如下:
③发送控制计数器
由于八位增量突发需要主机占用八个总线周期进行数据的传输,因此就需要发送控制计数器来控制发送的周期数。这里我使用[2:0]send_cnt来进行控制,从000到111一共八个状态,溢出自动清零,其控制逻辑如下所示:
④产生输出的组合逻辑
根据当前状态来进控制信号、地址和数据的传输。最关键的是在SEND状态的时候,主机需要判断从即的传输响应HRESP来进行下一步的传输,只有当HRESP为OKAY表示传输完成且HREADY为高电平表示从机准备好接收数据的时候才可以发送下一个数据。三个状态对应输出的代码如下所示:
本实验需要设计一个AHB主机接口模块,提供主机FIFO到AHB总线的接口,使得来自NoC经过解包的数据通过AHB总线发送到从机Memory中,其框图如下图所示:
同实验1,为了简化设计,不需要全面考虑所有可能的接口配置情况,只需要考虑HSIZE=3’b010即八位(一个字节)传输、HPROT=4’b0001即数据/预取指、HRESP为OKAY即传输成功的情况。
由于FIFO中第一个数据为NoC的源节点地址(32’h00000201),第二个数据为NoC的数据长度(32’h00000010),往后十六个数据才是需要写入Memory的数据(32’h00000011一直到32’h00000020),因此我让主机AHB_Master_FIFO采用一个十六位增量突发的方式将数据一次性写入从机Memory中。
采用三段式状态机来实现主机十六位增量传输情况的RTL实现,其状态转移图如下图所示。
①状态定义以及产生下一个状态的组合逻辑
主机AHB_Master_FIFO与实验3.1的大同小异。3.1的AHB_Master是通过en信号来使能的,而3.2的AHB_Master_FIFO是通过fifo_full信号来使能的。同时需要ADDR状态来判断从机地址;需要SIZE状态来判断突发类型。状态编码和状态转移组合逻辑代码如下所示:
②发送控制计数器
实验3.2中的AHB_Master_FIFO与AHB_Master的另一点不同在于控制寄存器send_cnt的位数变为了4位。因为采用十六位增量突发来发送连续十六个数据,因此需要send_cnt从0到15变化来控制主机占用总线发送数据的周期数。详细代码见【附录】。
③从机地址判断
在ADDR状态根据从FIFO接收到的data判断从机地址:
将AHB总线的三个模块:从机AHB_Slave、仲裁器AHB_Arbiter、主机AHB_Master整合到一个testbench中进行仿真,施加对应的en、data激励信号,仿真波形如下所示:
可以看到AHB_Master发起了一次成功的八位增量突发的传输,HADDR从32’h40000000一直递增到32’h4000001c,对应写数据总线从32’h00000008递增到32’h0000000f,传输类型HTRANS在第一个发送周期为NON_SEQ而网络七个发送周期都是SEQ,因此主机发送的信号时序正常。从机接收控制信号之后addr从8’h00递增到8’h07并同时发送对应的数据wr_data,查看Memory List后有如下所示结果:
可以看到在00到07连续八个地址上成功写入了发送的数据,表明主机发送成功。
将AHB总线的三个模块:从机AHB_Slave、仲裁器AHB_Arbiter、主机AHB_Master以及RAM和FIFO整合到一个testbench中进行仿真,施加对应的en、data激励信号,仿真波形如下所示:
可以看到AHB_Master发起了一次成功的十六位增量突发的传输,HADDR从32’h40000000一直递增到32’h4000007c,对应写数据总线从32’h000000201递增到32’h0000001e,传输类型HTRANS在第一个发送周期为NON_SEQ而网络十五个发送周期都是SEQ,因此主机发送的信号时序正常。从机接收控制信号之后addr从8’h10递增到8’h1f并同时发送对应的数据wr_data,查看Memory List后有如下所示结果:
可以看到在8’h40到8’h4f连续十六个地址上成功写入了发送的数据,表明主机发送成功。
module AHB_Master(
input en,//AHB主机接口使能端
input [31:0]data,//AHB主机接口数据输入端口
input HCLK,//总线时钟
input HRESETn,//总线复位
input HGRANT,//总线授予 选中1未选0
input HREADY,//传输完成
input [1:0]HRESP,//传输响应= OKEY
input [31:0]HRDATA,//读数据总线
output reg HBUSREQ,//总线请求
output reg HLOCK,//总线锁定
output reg [1:0]HTRANS,//传输类型
output reg [31:0]HADDR,//地址总线
output reg HWRITE,//传输方向 写1读0
output reg [2:0]HSIZE,//传输大小= 010
output reg [2:0]HBURST,//突发类型
output reg [31:0]HWDATA,//写数据总线
output reg [3:0]HPROT //保护控制= 0001
);
/******************************************************************
AHB总线参数定义开始
******************************************************************/
//传输响应参数定义(从机发出)
parameter HRESP_OKAY = 2'b00;//传输成功完成
parameter HRESP_ERROR = 2'b01;//传输错误
parameter HRESP_RETRY = 2'b10;//传输未完成 重试
parameter HRESP_SPLIT = 2'b11;//传输未完成
//传输类型参数定义(主机发出)
parameter HTRANS_IDLE = 2'b00;//空闲
parameter HTRANS_BUSY = 2'b01;//忙
parameter HTRANS_NONSEQ = 2'b10;//非连续
parameter HTRANS_SEQ = 2'b11;//连续
//突发类型参数定义(主机发出)
parameter HBURST_SINGLE = 3'b000;//单一传输
parameter HBURST_INCR = 3'b001;//未指定长度的增量突发
parameter HBURST_WRAP4 = 3'b010;//4拍回环突发
parameter HBURST_INCR4 = 3'b011;//4拍增量突发
parameter HBURST_WRAP8 = 3'b100;//8拍回环突发
parameter HBURST_INCR8 = 3'b101;//8拍增量突发
parameter HBURST_WRAP16 = 3'b110;//16拍回环突发
parameter HBURST_INCR16 = 3'b111;//16拍增量突发
/******************************************************************
AHB总线参数定义结束
******************************************************************/
//状态机状态编码
localparam IDLE = 3'b001;
localparam REQ = 3'b010;
localparam SEND = 3'b100;
reg [2:0]cstate,nstate;//状态寄存器定义
reg [2:0]send_cnt;//计数控制发送数据
reg [31:0]data_reg;//寄存数据
//计数控制逻辑
always@(posedge HCLK or negedge HRESETn)begin
if(!HRESETn)begin
send_cnt <= 3'd0;
end
else begin
case(cstate)
SEND:begin
if(HGRANT)begin//被授予总线使用权
send_cnt <= send_cnt + 3'd1;
end
else begin//未被授予总线使用权
send_cnt <= send_cnt;
end
end
default: send_cnt <= 3'd0;
endcase
end
end
//打一拍寄存数据,在地址周期之后的下一个周期发送
always@(posedge HCLK or negedge HRESETn)begin
if(!HRESETn)begin
data_reg <= 32'hxxxx_xxxx;
end
else begin
data_reg <= data;
end
end
//状态转移同步逻辑
always@(posedge HCLK or negedge HRESETn)begin
if(!HRESETn)begin
cstate <= IDLE;
end
else begin
cstate <= nstate;
end
end
//产生下一个状态组合逻辑
always@(*)begin
case(cstate)
IDLE:begin
nstate = en?REQ:IDLE;
end
REQ:begin
nstate = en?(HGRANT?SEND:REQ):IDLE;
end
SEND:begin
nstate = en?((&send_cnt)?IDLE:SEND):IDLE;//send_cnt溢出则回到IDLE状态
end
endcase
end
//产生输出组合逻辑
always@(*)begin
case(cstate)
IDLE:begin
HBUSREQ= 1'b0;
HLOCK = 1'b0;
HBURST = 3'bxxx;
HSIZE = 3'bxxx;
HPROT = 4'bxxxx;
HWRITE = 1'bx;
HTRANS = HTRANS_IDLE;
HADDR = 32'hxxxx_xxxx;
HWDATA = data_reg;
end
REQ:begin
HBUSREQ= 1'b1;
HLOCK = 1'bx;
HBURST = HBURST_INCR8;
HSIZE = 3'b010;
HPROT = 4'b0001;
HWRITE = 1'bx;
HTRANS = HTRANS_IDLE;
HADDR = 32'hxxxx_xxxx;//选择从机
HWDATA = data_reg;
end
SEND:begin
/*先地址周期再数据周期,因此最后需要额外一个数据周期
第一个地址周期数据无效,第九个数据周期地址无效*/
HBUSREQ= 1'b1;
HLOCK = 1'b1;
HBURST = HBURST_INCR8;
HSIZE = 3'b010;
HPROT = 4'b0001;
HWRITE = 1'b1;//写数据
case(HRESP)//判断传输响应
HRESP_OKAY:begin
if(HREADY)begin//如果从机准备好接收数据就发送
HTRANS = (|send_cnt)?HTRANS_SEQ:HTRANS_NONSEQ;
HWDATA = data_reg;
HADDR = 32'h4000_0000 + (send_cnt<<2);//以4为增量递增,保证地址对齐
end
end
HRESP_ERROR,
HRESP_RETRY,
HRESP_SPLIT:begin
HTRANS = HTRANS;
HWDATA = HWDATA;
HADDR = HADDR;
end
endcase
end
default:begin
HBUSREQ= 1'b0;
HLOCK = 1'b0;
HBURST = HBURST_INCR8;
HSIZE = 3'b010;
HPROT = 4'b0001;
HWRITE = 1'b0;
HTRANS = HTRANS_IDLE;
HADDR = 32'd0;
HWDATA = 32'd0;
end
endcase
end
endmodule
module AHB_Master_FIFO(
input fifo_full,//FIFO 满信号
input fifo_empty,//FIFO 空信号
input [31:0]data,//FIFO 读数据端口
output reg rd_en,//FIFO 读使能
input HCLK,//总线时钟
input HRESETn,//总线复位
input HGRANT,//总线授予 选中1未选0
input HREADY,//传输完成
input [1:0]HRESP,//传输响应= OKEY
input [31:0]HRDATA,//读数据总线
output reg HBUSREQ,//总线请求
output reg HLOCK,//总线锁定
output reg [1:0]HTRANS,//传输类型
output reg [31:0]HADDR,//地址总线
output reg HWRITE,//传输方向 写1读0
output reg [2:0]HSIZE,//传输大小= 010
output reg [2:0]HBURST,//突发类型
output reg [31:0]HWDATA,//写数据总线
output reg [3:0]HPROT //保护控制= 0001
);
/******************************************************************
AHB总线参数定义开始
******************************************************************/
//传输响应参数定义(从机发出)
parameter HRESP_OKAY = 2'b00;//传输成功完成
parameter HRESP_ERROR = 2'b01;//传输错误
parameter HRESP_RETRY = 2'b10;//传输未完成 重试
parameter HRESP_SPLIT = 2'b11;//传输未完成
//传输类型参数定义(主机发出)
parameter HTRANS_IDLE = 2'b00;//空闲
parameter HTRANS_BUSY = 2'b01;//忙
parameter HTRANS_NONSEQ = 2'b10;//非连续
parameter HTRANS_SEQ = 2'b11;//连续
//突发类型参数定义(主机发出)
parameter HBURST_SINGLE = 3'b000;//单一传输
parameter HBURST_INCR = 3'b001;//未指定长度的增量突发
parameter HBURST_WRAP4 = 3'b010;//4拍回环突发
parameter HBURST_INCR4 = 3'b011;//4拍增量突发
parameter HBURST_WRAP8 = 3'b100;//8拍回环突发
parameter HBURST_INCR8 = 3'b101;//8拍增量突发
parameter HBURST_WRAP16 = 3'b110;//16拍回环突发
parameter HBURST_INCR16 = 3'b111;//16拍增量突发
/******************************************************************
AHB总线参数定义结束
******************************************************************/
//状态机状态编码
localparam IDLE = 5'b00001;
localparam ADDR = 5'b00010;
localparam SIZE = 5'b00100;
localparam REQ = 5'b01000;
localparam SEND = 5'b10000;
reg [4:0]cstate,nstate;//状态寄存器定义
reg [3:0]send_cnt;//计数控制发送数据
reg [31:0]data_reg0,data_reg1;//打两拍延迟两个时钟周期作为数据周期
reg [31:0]base_addr;//产生基地址
//FIFO读写控制逻辑
// always@(posedge HCLK or negedge HRESETn)begin
always@(*)begin
if(!HRESETn)begin
rd_en <= 1'b0;
end
else if(fifo_full)begin
rd_en <= 1'b1;
end
else if(fifo_empty)begin
rd_en <= 1'b0;
end
else begin
rd_en <= rd_en;
end
end
//计数控制逻辑
always@(posedge HCLK or negedge HRESETn)begin
if(!HRESETn)begin
send_cnt <= 4'd0;
end
else begin
case(cstate)
SEND:begin
if(HGRANT)begin//被授予总线使用权
send_cnt <= send_cnt + 4'd1;
end
else begin//未被授予总线使用权
send_cnt <= send_cnt;
end
end
default: send_cnt <= 4'd0;
endcase
end
end
//在ADDR状态下判断基地址
always @(posedge HCLK or negedge HRESETn) begin
case (cstate)
ADDR: begin
case (HWDATA)
32'h00000101: base_addr <= 32'h20000000;
32'h00000201: base_addr <= 32'h40000000;
32'h00000301: base_addr <= 32'h10000000;
32'h00000401: base_addr <= 32'h30000000;
default: base_addr <= base_addr;
endcase
end
default: base_addr <= base_addr;
endcase
end
//打两拍暂存数据等到数据周期发送出去
always@(posedge HCLK or negedge HRESETn)begin
if(!HRESETn)begin
data_reg0 <= 32'd0;
data_reg1 <= 32'd0;
end
else begin
data_reg0 <= data;
data_reg1 <= data_reg0;
end
end
//状态转移同步逻辑
always@(posedge HCLK or negedge HRESETn)begin
if(!HRESETn)begin
cstate <= IDLE;
end
else begin
cstate <= nstate;
end
end
//产生下一个状态组合逻辑
always@(*)begin
case(cstate)
IDLE:nstate = fifo_full?ADDR:IDLE;
ADDR:nstate = SIZE;
SIZE:nstate = REQ;
REQ:nstate = HGRANT?SEND:REQ;
SEND:nstate = (&send_cnt)?IDLE:SEND;
endcase
end
//产生输出组合逻辑
always@(*)begin
case(cstate)
REQ:begin
HBUSREQ= 1'b1;
HLOCK = 1'b0;
HBURST = HBURST_INCR16;
HSIZE = 3'b010;
HPROT = 4'b0001;
HWRITE = 1'b1;
HTRANS = HTRANS_IDLE;
HADDR = 32'hxxxx_xxxx;
HWDATA = data;
end
SEND:begin
/*先地址周期再数据周期,因此最后需要额外一个数据周期
第一个地址周期数据无效,第九个数据周期地址无效*/
HBUSREQ= 1'b1;
HLOCK = 1'b1;
HBURST = HBURST_INCR16;
HSIZE = 3'b010;
HPROT = 4'b0001;
HWRITE = 1'b1;//写数据
case(HRESP)//判断传输响应
HRESP_OKAY:begin
if(HREADY)begin//如果从机准备好接收数据就发送
HTRANS = (|send_cnt)?HTRANS_SEQ:HTRANS_NONSEQ;
HWDATA = data_reg1;
HADDR = base_addr + (send_cnt<<2);//以4为增量递增,保证地址对齐
end
end
HRESP_ERROR,
HRESP_RETRY,
HRESP_SPLIT:begin
HTRANS = HTRANS;
HWDATA = HWDATA;
HADDR = HADDR;
end
endcase
end
default:begin
HBUSREQ= 1'b0;
HLOCK = 1'b0;
HBURST = 3'bxxx;
HSIZE = 3'bxxx;
HPROT = 4'bxxxx;
HWRITE = 1'bx;
HTRANS = HTRANS_IDLE;
HADDR = 32'hxxxx_xxxx;
HWDATA = data;
end
endcase
end
endmodule