AHB接口的特定传输操作MASTER模块
//题目:AHB接口的模拟MASTER模块
//设计内容:
//针对给定的AMBA AHB SRAM Slave接口模块,设计特定传输操作的Master模块
//设计指标:
//AMBA AHB2.0 接口
//32bit 数据位宽
//先写入数据,后读出数据确认
//传输要求1:0x0 ‐> 0x8, INCR
//传输要求2:0x10‐> ?, INCR4
//传输要求2:0x28‐> ?, WRAP8
//实验内容:
//完成RTL设计
//完成TESTBENCH设计
//完成设计验证文档
目录
我的源码链接
1.介绍AMBA2.0总线
总线设备
总线的信号
总线传输的两个阶段
2. 设计思路解析
读写数据的赋值规则
位地址控制
3.代码问题分析
注:部分代码来自于https://mp.weixin.qq.com/s/P4jevRrkga2DF93UPXLUJg
说明:
1 如果你想使用或在我的代码基础上debug,可到我主页下载https://download.youkuaiyun.com/download/qq_43029779/85274862?spm=1001.2014.3001.5503
代码部分综述:
ahb_top.v是连接ahb_master, ahb2sram, fpga_sram的顶层设计文件
ahb_top_tb.v是整个工程的testbench
Master.v是借鉴网上代码所写,仍有许多问题,,
如地址没能确定完全读入至SRAM内之后再自增,1kb边界trans发noseq的问题
Master.v分为数据传输部分,地址控制部分,trans数据转换状态机.
2 同时,另一份AHB_master代码,通过脑动写入数据和地址模拟完成ahbmaster操作也可在我主页下载https://download.youkuaiyun.com/download/qq_43029779/85274937?spm=1001.2014.3001.5503
3 代码仅用于学习交流使用
1.介绍AMBA2.0总线
典型的soc结构如下
高速总线ahb通过apb_bridge连接带apb接口的slave。
多个master通过arbiter仲裁器仲裁主机并传输地址和数据,slave通过decoder译码器来向主机传输信息。
总线设备
1.1 AHB 主设备(master)
初始化一次读/写操作
某一时刻只允许一个主设备使用总线
uP、DMA、DSP、LCDC …
1.2 AHB从设备(slave)
响应一次读/写操作
外部存储器控制器EMI、APB bridge、UART、 …
1.3 AHB仲裁器(arbiter)
允许某一个主设备控制总线
在AMBA协议中没有定义仲裁算法
1.4 AHB译码器(decoder)
通过地址译码来决定选择哪一个从设备
总线的信号
Hclk是主机发出的时钟;hresetn是主机发出的复位信号;haddr32位地址线;
htrans传输的状态:NONSEQ、SEQ、IDLE、BUSY
hwrite 写使能信号,1写,0读
hsize传输的数据位宽:8、16、32
hburst传输的burst类型,single、incr4/8/16、wrap4/8/16
//代码部分会详细介绍如何计算incr以及wrap
hprot保护控制信号;hwdata32位写数据信号;hselx选择从机信号
hrdata32位读数据信号,hready由从机发出给主机,告诉主机 从机是否准备好 高:从设备指出传输结束/ 低:从设备需延长传输周期
hresp是从机发出的状态,表示他对主机的应答情况OKAY、ERROR、RETRY、SPLIT
总线传输的两个阶段
地址周期(地址和控制信号),只有一个cycle
数据周期(读数据/写数据),由HREADY信号决定
需要几个cycle
流水线传送
先是地址周期,然后是数据周期
2. 设计思路解析
项目已经给我们了sram, apb转接module,要求是设计一个ahb_master,即我们需要通过模拟建立一个主机,通过apb接口向从机sram发送控制信号,最后通过top文件将三个module连接。
首先设计端口如下
在这里插入代码片
module AHB_master(
//---------- 输入:系统信号 ----------
// HGRANTx,//指出主设备x可访问总线(ARBITER发出)- 主设备x控制总线:HGRANTx=1且HREADY=1
HCLK,
HRESETn,//systenm signal
//-------- 输入 响应信号:s->M -----------------
HRESP,//从设备发给主设备的总线传输状态 OKEY、ERROR、RETRY、SPLIT
HREADYOUT,//高:从设备指出传输结束 - 低:从设备需延长传输周期
//-------- 输入 :地址和控制信号-----------------
in_ADDR,
in_TRANS,
in_WRITE,
in_SIZE,
in_BURST,
//---------- 输入:读数据信号 --------------
HRDATA,//读数据总线,从从设备读到主设备
//-------- 输出:地址和控制信号----------------
HADDR,//32位系统地址总线
HTRANS,//传输的状态:00;IDLE、01;BUSY、10;NONSEQ、11;SEQ
HWRITE,
HSIZE,//传输的数据位宽:8、16、32
HBURST,//传输的burst类型,single、incr4/8/16、wrap4/8/16
// HPROT,
//---------- 输出:写数据信号 --------------
HWDATA//写数据总线,从主设备写到从设备
// HSELx,//指明当前传输被访问的从设备,来自译码器
);
然后定义各信号的参数
在这里插入代码片
// ------------- 定义参数 ----------------------
//传输类型:HTRANS
parameter SRAM_IDLE=00;
parameter SRAM_BUSY=01;
parameter SRAM_NOSEQ=10;
parameter SRAM_SEQ=11;
//传输的burst类型
parameter single = 000;
parameter incr = 001;
parameter wrap4 = 010;
parameter incr4 = 011;
parameter wrap8 = 100;
parameter incr8 = 101;
parameter wrap16 = 110;
parameter incr16 = 111;
//从设备发给主设备的总线传输状态:HRESP
parameter OKAY = 00;
parameter ERROR = 01;
parameter RETRY = 10;
parameter SPLIT = 11;
//传输的 Hsize 类型
parameter size_1 = 000;
parameter size_2 = 001;
parameter size_4 = 010;
parameter size_8 = 011;
parameter size_16 = 100;
parameter size_32 = 101;
parameter size_64 = 110;
parameter size_128 = 111;
parameter state_idle = 0, state_write = 1, state_read = 2;
下面这一段是读写数据的赋值规则
1.如果是读操作就就把接收到的数据给rec_data
2.如果不复位就开启burst传输的方式,并选择传输字节数,其中,如果是写操作&从机不忙 就把数据写入data
否则保持数据。
在这里插入代码片
always @ (negedge H_clk)begin
if(!i_WR && !busy)
rec_data <= R_data;
end
always @ (posedge H_clk , negedge H_rstN)
begin
if (!H_rstN)
begin
//WR <= 1'b0;
size <= 3'b0;
burst <= 4'b0;
data <= 0;
data_1 <= 0;
end
else if (H_readyN)responce check
begin
//WR <= i_WR;
size <= i_size;
burst <= i_burst;
if (i_WR && !busy)
begin //data is chnaging in busy so i need control here
data_1 <= i_data;//one cycle late data
data <= data_1;
end
else
data <= data_1; //also make it xx
end
else begin
data_1 <= data_1; //one cycle late data
data <= data;
end
end
以下一段位地址控制,如果从机状态是HTRANS_NONSEQ,那么就将add <= i_add,
地址i_add [12:0]给initial_add赋值,这里只取13位,2^12=8kb(这里设置initial_add的原因是后面要通过burst传输和hsize计算地址)
temp_counter 是计算burst传输次数的节拍器,每传输一次数据,节拍器就减一;
cal_size是hsize定义的字节长度
余下的类别就是single、incr4/8/16、wrap4/8/16
single最简单,直接传地址+数据;
incr也简单,直接地址=上一次地址+cal_size即可,但这里没有定义停止incr的问题,也没有注意跨越1kb边界问题。(下文分析了问题)
wrap的计算较为复杂,出去计算 地址以 hsize定义的字节长度 自增外,还要计算回环的首地址与回环的地址长度bound
在这里插入代码片
//address control
always @ (posedge H_clk or negedge H_rstN)
begin
if (!H_rstN)
begin add <= 32'b0;WR<=0;end
else if (H_readyN && trans_next == HTRANS_NONSEQ) //single
begin
initial_add <= i_add [12:0]; //13位,8kb,1kB
add <= i_add;
WR<=i_WR; //single just sample
temp_counter <= counter;// beats
end
//&& trans_next == HTRANS_SEQ
else if(H_readyN &&(!busy) && i_burst == INCR ) //check dependency on H_readyN and also restrict 1024KB size for future
begin add <= add + cal_size;//i_size determine cal_size
WR<=i_WR;
end
//incr not define length,may cause 1KB problem
else if(H_readyN &&(!busy) && temp_counter != 0 && trans_next == HTRANS_SEQ //wrap burst
&& (i_burst == WRAP4 | i_burst == WRAP8 | i_burst == WRAP16 ))
begin
temp_counter <= temp_counter -1;//拍数减1
if((add[12:0]+cal_size) >= wrap_bound)//超越边界,减去传输大小,spec
add <={add[31:13],((add[12:0]+ cal_size) - trans_size)};
else
add <={add[31:13],(add[12:0] + cal_size)};//size
end
else if(H_readyN &&(!busy) && temp_counter != 0 && trans_next == HTRANS_SEQ
&& (i_burst == INCR4 | i_burst == INCR8 | i_burst == INCR16 ))
begin
add <= (add + cal_size); //size
temp_counter <= temp_counter -1;
end
else if ((i_burst == SINGLE)||(i_trans == HTRANS_IDLE))
temp_counter <= 0;
else
begin
add <= add;
temp_counter <= temp_counter;//maintain
end
end
1 先计算块传输的大小的trans_size ,再计算开始地址是块传输的倍数,
2 Start_add是块传输的理论的起始地址
3 Warpbound是warp的边界,若达到该地址则改从Start_add开始传输。
例如:计算hsize=8BYTE,burst=wrap8,则trans_size =8*8=64=2^6,起始地址应该是(当前地址除以64) 的整倍数(即该首地址是第几个block)再乘trans_size(即一个block的大小) 。
在这里插入代码片
//combination ADDRESS logic calculation
always @ (cal_burst,i_size,initial_add,shifty,cal_size)
begin
trans_size = cal_burst * cal_size;
start_add = ((initial_add >> shifty)*(trans_size)); //synthesis doesn't provide divide operator
wrap_bound = start_add + trans_size ;
end
3.代码问题
功能1:burst= incr4
在起始地址0x22开始,输入四个数据12345678,88884444,44442222,22221111,
可以通过HRDATA读出数据流水线写入, 同时应该trans从开始发出noseq,seq,seq,seq,idle,但目前trans时序还不太正确。
功能2:burst= warp8
写入数据时,地址能够自动按照size和burst自增,但没能确定完全读入至SRAM内之后再自增。
数据读取正常,读出的数据正常,回环经历了覆盖历史数据。
功能3:incr
目前手地址无法写入,也没有解决1kb边界trans发noseq的问题,待后续修改。
代码部分综述:
ahb_top.v是连接ahb_master, ahb2sram, fpga_sram的顶层设计文件
ahb_top_tb.v是整个工程的testbench
Master.v是借鉴网上代码所写,仍有许多问题,,
如地址没能确定完全读入至SRAM内之后再自增,1kb边界trans发noseq的问题
Master.v分为数据传输部分,地址控制部分,trans数据转换状态机.
如果reset就赋初值,
如果是incr就地址逐增size
如果是wrap就计算bound值
上图代码表示先计算块传输的大小的bound,再计算开始地址是块传输的倍数,
Start_add是块传输的理论的起始地址
Warpbound是warp的边界,若达到该地址则改从Start_add开始传输。
trans数据转换状态机部分。
应增加:(来解决跨越1kb边界问题,即,trans发一个noseq,将块传输变为一个新的incr)
if(H_readyN &&(!busy) && trans_next == HTRANS_SEQ && i_burst == INCR &&(initial_add[9:0]+cal_size-1 ==9’h3ff) )begin
trans_next = HTRANS_NONSEQ;
另:更改了ahb_to_sram的地址问题:由于其中addr{1:0}用来作为判断字节通道的参数没有被后续加入HADD【13:0】里,但这样会导致地址不是16位,无法在SRAM中寻址,故,统一将地址改为16位,
另:sram中逻辑问题:判断RDATA=merge1?Sramdata:buffer,
会导致数据RDATA一直有值(即使在writen=1时),在这里改成了用always块判断!writen再赋值。