Altera PCI IP target设计分享
最近调试也有关于使用Altera 家的PCI IP,然后分享一下代码:
主要实现:主控作为主设备,FPGA作为从设备,主控对FPGA IO读写的功能
后续会分享FPGA作为主设备, 从 FPGA通过 memory写到主控内存,会涉及到DMA的设计。
设计核心:
- 触发lt_framen 和 lt_tsr[5:0] 与 BAR 范围命中相对应位通知本地端设备它正在声明读取事件或者写入事件 ,lt_tsr[0]被触发,表示BAR0命中
- lt_tsr[7] 被置位,表示待处理的事务是 64位
- lt_tsr[8] 被置位,表示pci ip 功能的pci侧在忙
- lt_tsr[10] 被置位,表示上一个时钟周期期间PCI端数据传输成功
- 取消断言lt_tsr[11:0]通知本地端设备事务已完成
具体参考PCI IP手册:
使用PCI IP的 下面模式:
参考时序:
IO写:
IO读:
具体RTL 代码:
`timescale 1 ns / 1 ns
//
// Company:
// Engineer: felixgao
//
// Create Date:
// Design Name:
// Module Name:
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// date version author
//
//
// note :
//
// 1. 触发lt_framen 和 lt_tsr[5:0] 与 BAR 范围命中相对应位通知本地端设备它正在声明读取事件或者写入事件 ,lt_tsr[0]被触发,表示BAR0命中
// 2. lt_tsr[7] 被置位,表示待处理的事务是 64位
// 3. lt_tsr[8] 被置位,表示pci ip 功能的pci侧在忙
// 4. lt_tsr[10] 被置位,表示上一个时钟周期期间PCI端数据传输成功
// 5. 取消断言lt_tsr[11:0]通知本地端设备事务已完成
//module pci_local_target(input i_clk ,input i_rstn ,output o_lt_abortn , output o_lt_discn , output o_lt_rdyn , input i_lt_framen , input i_lt_ackn , input i_lt_dxfrn , input [11:0] i_lt_tsr , input [31:0] i_l_dat , input [31:0] i_l_adr , input [3:0 ] i_l_ben , input [3:0] i_l_cmd , output [31:0] o_l_adi , output [3:0] o_l_cben ,// io write data port for userinput i_io_wrready ,output [31:0] o_io_wrdata ,output o_io_wrvalid ,output [31:0] o_io_wr_addr ,output [31:0] o_io_rd_addr ,// io read data port for userinput [31:0] i_io_rddata ,input i_io_rdvalid ,output reg o_io_rdready );parameter S_IDLE = 4'b0000 , //Idle S_WAIT = 4'b0001 , S_TRGT_WR = 4'b0010 , //Target Write S_TRGT_RD = 4'b0100 , //Target Read S_END = 4'b1000 ; wire w_target_write ;
wire w_target_read ;
wire w_io_write ;
wire w_io_read ;
wire w_lt_ackn_rise ;
wire w_lt_dxfrn_rise ;
wire w_target_done ;
wire w_frame_fall ;
wire w_data_trans_success ;reg [3:0] r_cu_state ;
reg [3:0] r_nx_state ;
reg [3:0] r_sta_cnt ;
reg ri_lt_ackn ;
reg ri_lt_dxfrn ;
reg r_done ;
reg ri_lt_framen ;
reg [31:0] ro_io_wrdata ;
reg ro_io_wrvalid ;
reg [31:0] ro_io_wr_addr ;
reg [31:0] ro_io_rd_addr ;
reg [31:0] ro_l_adi ;
reg r_iowrite_it_rdyn ;
reg r_ioread_it_rdyn = 0 ;
reg r_switch_itrdyn ;assign w_frame_fall = i_lt_framen && !ri_lt_framen ; // Get the falling edge of the i_lt_framen
assign w_io_write = !i_lt_framen && ( i_l_cmd == 3) && i_lt_tsr[0] ; // 接收 来自 主设备的 IO 写 命令,i_lt_tsr[0]为高同时BAR0 被选择
assign w_io_read = !i_lt_framen && ( i_l_cmd == 2) && i_lt_tsr[0] ; // 接收 来自 主设备的 IO 读 命令,i_lt_tsr[0]为高同时BAR0 被选择
assign w_target_read = w_io_read ;
assign w_target_write = w_io_write ;
assign w_lt_ackn_rise = i_lt_ackn && !ri_lt_ackn ; // 获取上升沿 此信号是 pci_mt32 IP输出
assign w_lt_dxfrn_rise = i_lt_dxfrn && !ri_lt_dxfrn ; // 获取上升沿 此信号是 pci_mt32 IP输出
assign w_target_done = !i_lt_tsr[8] && r_done ; // 事务传输完成
assign o_io_wrdata = ro_io_wrdata ; // 接收到主设备 向从设备 写的数据
assign o_io_wrvalid = ro_io_wrvalid ; // 接收到主设备 向从设备 写的数据有效
assign o_io_wr_addr = ro_io_wr_addr ; // 接收到主设备 向从设备 传来的地址,指示向什么地址写数据
assign o_io_rd_addr = ro_io_rd_addr ; // 接收到主设备 向从设备 传来的地址,指示向什么地址读数据
assign o_l_cben = 4'h0 ; // 用户给的 字节数据使能 ,每个字节默认都有效
// assign o_io_rdready = (r_cu_state == S_TRGT_RD && r_sta_cnt == 0) ? 1'b1:1'b0 ; // 用户 读数据准备信号
// assign o_io_rdready = (i_l_cmd == 2) ? 1'b1:1'b0 ;
assign o_l_adi = ro_l_adi ;
assign o_lt_abortn = 1 ; // 从设备默认不终止
assign o_lt_discn = 1 ; // 从设备默认不断连
assign w_data_trans_success = !i_lt_ackn && !i_lt_dxfrn ; // 数据传输一次成功标志
assign o_lt_rdyn = (r_switch_itrdyn)?r_ioread_it_rdyn:r_iowrite_it_rdyn ; // 用户准备 接收 或者 发送数据//准备好读数据,送到pci ip
always @ (posedge i_clk or negedge i_rstn)
beginif (!i_rstn)o_io_rdready <= 0;else if (i_l_cmd == 2)o_io_rdready <= 1; else o_io_rdready <= 0;
endalways @ (posedge i_clk or negedge i_rstn)
beginif (!i_rstn) beginri_lt_framen <= 1;ri_lt_ackn <= 1;ri_lt_dxfrn <= 1;end else beginri_lt_framen <= i_lt_framen;ri_lt_ackn <= i_lt_ackn ;ri_lt_dxfrn <= i_lt_dxfrn ; end
end// 确保传输完成,能够接收到 pci_mt32的 dxfrn 和 ackn 信号
always @ (posedge i_clk or negedge i_rstn)
beginif (!i_rstn)r_done <= 0;else if (r_cu_state == S_WAIT) r_done <= 0; else if (w_lt_ackn_rise && w_lt_dxfrn_rise)r_done <= 1; else r_done <= r_done;
end// 从模式 三段式状态机
always @ (posedge i_clk or negedge i_rstn)
beginif (!i_rstn)r_cu_state <= S_IDLE;else r_cu_state <= r_nx_state;
endalways @ (*)
begincase(r_cu_state)S_IDLE : begin // 空闲状态等待10周期,状态跳转if (r_sta_cnt == 10)r_nx_state = S_WAIT;else r_nx_state = r_cu_state; end S_WAIT : begin // 当接收到主设备写或者读命令时,状态跳转if (w_target_write) r_nx_state = S_TRGT_WR;else if (w_target_read)r_nx_state = S_TRGT_RD; else r_nx_state = r_cu_state; end S_TRGT_WR : begin // 写传输结束,状态跳转if (w_target_done)r_nx_state = S_END; else r_nx_state = r_cu_state; end S_TRGT_RD : begin // 读传输结束,状态跳转if (w_target_done)r_nx_state = S_END; else r_nx_state = r_cu_state; endS_END : r_nx_state = S_WAIT ;default : r_nx_state = S_IDLE ; endcase
end//状态计数器
always @ (posedge i_clk or negedge i_rstn)
beginif (!i_rstn)r_sta_cnt <= 0;else if (r_cu_state != r_nx_state)r_sta_cnt <= 0; else r_sta_cnt <= r_sta_cnt + 1'b1;
end// rdyn 切换控制信号,IO写和 IO读 控制lt_rdyn方式不一样
always @ (posedge i_clk or negedge i_rstn)
beginif (!i_rstn)r_switch_itrdyn <= 0;else if (w_target_write)r_switch_itrdyn <= 0; else if (w_target_read)r_switch_itrdyn <= 1; else r_switch_itrdyn <= r_switch_itrdyn;
end// IO 写操作时,lt_rdyn的控制
always @ (posedge i_clk or negedge i_rstn)
beginif (!i_rstn)r_iowrite_it_rdyn <= 1;else if (r_cu_state == S_TRGT_WR && r_sta_cnt == 0)r_iowrite_it_rdyn <= 0; else if (r_cu_state == S_TRGT_WR && w_data_trans_success)r_iowrite_it_rdyn <= 1; else r_iowrite_it_rdyn <= r_iowrite_it_rdyn;
end// IO 读操作时,lt_rdyn的控制
always @ (*)
beginif (r_nx_state == S_END)r_ioread_it_rdyn <= 1; else if (r_cu_state == S_TRGT_RD)r_ioread_it_rdyn <= 0; else r_ioread_it_rdyn <= r_ioread_it_rdyn;
end// 主设备 写到 从设备的 数据 及 数据有效
always @ (posedge i_clk or negedge i_rstn)
beginif (!i_rstn) beginro_io_wrdata <= 0;ro_io_wrvalid <= 0;end else if (r_cu_state == S_TRGT_WR && w_data_trans_success && i_io_wrready) beginro_io_wrdata <= i_l_dat;ro_io_wrvalid <= 1;end else beginro_io_wrdata <= 0;ro_io_wrvalid <= 0; end
end// 主设备IO写操作时 写到 从设备的 地址
always @ (posedge i_clk or negedge i_rstn)
beginif (!i_rstn) beginro_io_wr_addr<= 0;end else if (r_cu_state == S_TRGT_WR && r_sta_cnt == 0) beginro_io_wr_addr <= i_l_adr;end else beginro_io_wr_addr <= ro_io_wr_addr;end
end// 主设备IO读操作时, 写到 从设备的 地址
always @ (posedge i_clk or negedge i_rstn)
beginif (!i_rstn) beginro_io_rd_addr<= 0;end else if (i_l_cmd == 2) beginro_io_rd_addr <= i_l_adr;end else beginro_io_rd_addr <= ro_io_rd_addr;end
end//从设备 发送给主设备数据
always @ (posedge i_clk or negedge i_rstn)
beginif (!i_rstn)ro_l_adi <= 0;else if (r_cu_state == S_TRGT_RD && !o_lt_rdyn && i_io_rdvalid && !i_lt_ackn)ro_l_adi <= i_io_rddata; else ro_l_adi <= ro_l_adi;
endendmodule