网站托管服务适合用于哪种类型的网站,京东商城网站建设,个人做网站郊游的英,有网络但浏览器打不开网页AMBA总线--APB协议时序及Verilog实现 1 APB3协议1.1 APB3时序1.1.1 APB写操作1.1.2 APB读操作 2 代码2.1 apb_master2.2 apb_slave 【博客首发于微信公众号《漫谈芯片与编程》#xff0c;欢迎专注一下#xff0c;多谢大家】 AMBA总线是用于连接微控制器和外围设备的总线协议欢迎专注一下多谢大家】 AMBA总线是用于连接微控制器和外围设备的总线协议由ARM公司提出的一种片上系统SoC互联标准。 AMBA总线协议家族主要包括APB、AHB、AXI、ACI、CHI协议其中后两个协议主要是专门为了缓存一致性
本篇博客主要介绍 APB3协议
1 APB3协议
APB3协议APB用来实现与外围设备进行通信–一般用来配置寄存器APB协议是一主多从唯一的master一般是apb_bridge模块 总体特点低功耗、低带宽、无流水线结构、每次传输至少需要两个时钟周期
1.1 APB3时序
在这里会分别给出有等待和无等待的时序图做对比在项目中主要还是用的有等待时序图 什么是无等待即slave会直接在maste拉高penable时进行拉高只维持一个周期 什么是有等待即slave会跟自己内部业务进行任意时刻拉高pready而在此期间以上master的信号都要维持不变
1.1.1 APB写操作 【描述】 1.T0周期(T0-T1)IDLE状态 2.T1周期(T1-T2)master提前准备好pasel,paddr,pwrite,pwdata; 3.T2周期(T2-T3)master拉高penable告知当前数据有效进行采样传输 –T3周期(T3-T4)是代表数据传输结束回到IDLE状态
1.1.2 APB读操作 T0周期IDLE状态 T1周期master提前准备好psel,paddr,pwrite; T2周期在无等待模式下slave准备好prdate并拉高pready当然在有等待模式下pready可以继续没准备好当pready准备好拉高后prdata同时返回并有效
2 代码
从前面系列篇可知对待时序我们尽量用状态机来设计指导编码 在这里官方给出状态机正好对应上面时序图上的周期
IDLEdeault state; SetUp:psel立刻拉高相关信号在setup阶段准备好并且下一周期就立刻转移到下一状态 Access:penable在该状态拉高相关信号paddr,pwrite,psel,pwdata等在这保持
2.1 apb_master
在这里实现一个apb_master的代码示例
/*-------------------------------------------------------------
-- modified by xlinxdu, 2022/05/27
-- pclk 50MHz
-- APB3,No pslverr signal
-- cmd_i:56bit;[55:48]:r/w ,8b0 - read,8b1 - write[47:32]:paddr ,[31:0]:pwdata
-------------------------------------------------------------*/
module apb
#(parameter RD_FLAG 8b0 ,parameter WR_FLAG 8b1 ,parameter CMD_RW_WIDTH 8 ,parameter CMD_ADDR_WIDTH 16 ,parameter CMD_DATA_WIDTH 32 ,parameter CMD_WIDTH CMD_RW_WIDTH CMD_ADDR_WIDTH CMD_DATA_WIDTH
)(
//-- system signalinput pclk_i ,input prst_n_i ,//-- cmd_ininput [CMD_WIDTH-1:0] cmd_i ,input cmd_vld_i ,output reg [CMD_DATA_WIDTH-1:0] cmd_rd_data_o,//-- apb interfaceoutput reg [CMD_ADDR_WIDTH-1:0] paddr_o ,output reg pwrite_o ,output reg psel_o ,output reg penable_o ,output reg [CMD_DATA_WIDTH-1:0] pwdata_o ,input [CMD_DATA_WIDTH-1:0] prdata_i ,input pready_i ,input pslverr_i
);//-- FSM state
parameter IDLE 3b001;
parameter SETUP 3b010;
parameter ACCESS 3b100;//-- current state and next state
reg [2:0] cur_state;
reg [2:0] nxt_state;//-- data buf
reg start_flag ;
reg [CMD_WIDTH-1:0] cmd_in_buf ;
reg [CMD_DATA_WIDTH-1:0] cmd_rd_data_buf;/*-----------------------------------------------\-- update cmd_in_buf --
\-----------------------------------------------*/
always (posedge pclk_i or negedge prst_n_i) beginif (!prst_n_i) begincmd_in_buf {(CMD_WIDTH){1b0}};endelse if (cmd_vld_i pready_i) begincmd_in_buf cmd_i;end
end/*-----------------------------------------------\-- start flag of transfer --
\-----------------------------------------------*/
always (posedge pclk_i or negedge prst_n_i) beginif (!prst_n_i) beginstart_flag 1b0;endelse if (cmd_vld_i pready_i) beginstart_flag 1b1;endelse beginstart_flag 1b0;end
end/*-----------------------------------------------\-- update current state --
\-----------------------------------------------*/
always (posedge pclk_i or negedge prst_n_i) beginif (!prst_n_i) begincur_state IDLE;endelse begincur_state nxt_state;end
end/*-----------------------------------------------\-- update next state --
\-----------------------------------------------*/
always (*) begincase(cur_state)IDLE :if(start_flag)beginnxt_state SETUP;endelse beginnxt_state IDLE;endSETUP :nxt_state ACCESS;ACCESS:if (!pready_i)beginnxt_state ACCESS;endelse if(start_flag)beginnxt_state SETUP;endelse if(!cmd_vld_i pready_i)beginnxt_state IDLE;endendcase
end/*-----------------------------------------------\-- update signal of output --
\-----------------------------------------------*/
always (posedge pclk_i or negedge prst_n_i) beginif (!prst_n_i) beginpwrite_o 1b0;psel_o 1b0;penable_o 1b0;paddr_o {(CMD_ADDR_WIDTH){1b0}};pwdata_o {(CMD_DATA_WIDTH){1b0}};endelse if (nxt_state IDLE) beginpsel_o 1b0;penable_o 1b0;endelse if(nxt_state SETUP)beginpsel_o 1b1;penable_o 1b0;paddr_o cmd_in_buf[CMD_WIDTH-CMD_RW_WIDTH-1:CMD_DATA_WIDTH];//-- readif(cmd_in_buf[CMD_WIDTH-1:CMD_WIDTH-8] RD_FLAG)beginpwrite_o 1b0;end//-- writeelse beginpwrite_o 1b1;pwdata_o cmd_in_buf[CMD_DATA_WIDTH-1:0];endendelse if(nxt_state ACCESS)beginpenable_o 1b1;end
end/*-----------------------------------------------\-- update cmd_rd_data_buf --
\-----------------------------------------------*/
always (posedge pclk_i or negedge prst_n_i) beginif (!prst_n_i) begincmd_rd_data_buf {(CMD_DATA_WIDTH){1b0}};endelse if (pready_i psel_o penable_o) begincmd_rd_data_buf prdata_i;end
end/*-----------------------------------------------\-- update cmd_rd_data_o --
\-----------------------------------------------*/
always (posedge pclk_i or negedge prst_n_i) beginif (!prst_n_i) begincmd_rd_data_o {(CMD_DATA_WIDTH){1b0}};endelse begincmd_rd_data_o cmd_rd_data_buf;end
endendmodule
2.2 apb_slave
在这里实现一个apb_slave读写寄存器的Demo;
timescale 1ns/100psmodule apb_slave #(parameter AW 32,//地址总线宽度parameter DW 32 //数据总线宽度) (//systeminput reset_n,//apb_interfaceinput pclk, //时钟input [AW-1:0] paddr, //地址input pwrite, //读/写控制信号input psel, //选择信号input penable, //使能信号input [DW-1:0] pwdata, //写(输入)数据output reg [DW-1:0] prdata, //读(输出)数据output reg pready, //传输完成标志low未完成high完成output reg pslverr //错误标志high出现错误);
//apb状态机
parameter IDLE 2b00;
parameter SETUP 2b01;
parameter ACCESS 2b10;
reg [1:0] state_now 2b00;
reg [1:0] state_next 2b00;//映射到总线上的寄存器
reg [31:0] readonly32; //0x10000000只可读
reg [15:0] readonly16; //0x10000004只可读
reg [31:0] readwrite32; //0x10000008可读可写
reg [15:0] readwrite16; //0x1000000C可读可写always (posedge pclk or negedge reset_n) begin: init_and_change_stateif (!reset_n) beginpready 1b0;pslverr 1b0;prdata 32h0;readwrite32 32h0;readwrite16 16h0;readonly32 32h12345678;readonly16 16habcd;state_now IDLE;endelse beginstate_now state_next;end
end//! fsm_extract
always (posedge pclk) begin :main_fsm_of_apbcase (state_now)IDLE: begin //IDLE状态case (psel)1b1: state_next SETUP; //选择本apb外设进入setup状态1b0: state_next IDLE;default: state_next IDLE;endcaseendSETUP: begin//SETUP状态case (psel)1b1: begincase (penable)1b1: state_next ACCESS;//选中且enable信号拉高进入ACCESS模式传输数据1b0: state_next SETUP;default: state_next SETUP;endcaseend1b0: state_next IDLE; //未被选中回到IDLEdefault: state_next IDLE;endcasepready 1b0;endACCESS: begin //ACCESS状态根据寄存器地址读写prdata 32h0;pslverr 1b0;if (pwrite) begin //wirte信号拉高写入模式case (paddr)32h10000008: begin readwrite32 pwdata; pready 1b1; end//数据传输完成 32h1000000C: begin readwrite16 pwdata[15:0]; pready 1b1; end//数据传输完成 default: pslverr 1b1;endcaseendelse if(!pwrite) begin //write信号拉低读取模式case (paddr)32h10000000: begin prdata readonly32; pready 1b1; end//数据传输完成 32h10000004: begin prdata readonly16; pready 1b1; end//数据传输完成 32h10000008: begin prdata readwrite32; pready 1b1; end//数据传输完成 32h1000000C: begin prdata readwrite16; pready 1b1; end//数据传输完成 default: pslverr 1b1;endcaseendif (pready) begincase (psel)1b1: state_next SETUP; //不需要传输数据回到SETUP1b0: state_next IDLE; //未被选中回到IDLEdefault: state_next IDLE;endcaseendelse state_next ACCESS; //需要继续传输数据则回到ACCESS enddefault: state_next IDLE;endcase
end
endmodule
[ref] 1.https://blog.csdn.net/qq_43244515/article/details/124968189 2.https://www.amghank.cn/2021/08/24/CORE%E5%AD%A6%E4%B9%A0%EF%BC%9AAMBA3–APB%E6%80%BB%E7%BA%BF%E5%8D%8F%E8%AE%AE%E5%8F%8A%E7%AE%80%E5%8D%95%E4%BE%8B%E5%AD%90/