本文分享Altera Fpga的 PCI IP master 设计思路:功能是FPGA作为主设备,向内存或者其它外设写入数据。本文代码未提供更完全功能以及优化的内容,可以继续优化完善,感兴趣的同学,可以自行完善。下面简单介绍一下设计。
1、需要熟悉PCI IP 手册上的时序关系,本文设计的是mem 写操作,单周期模式
2、注意申请总线时机,以及处理好 别的设备IO读操作FPGA时的数据冲突
3、DMA设计,其实比较简单,因人而异,本文只是每次传输一次数据发起DMA传输,达到目的字节长度,结束传输,等待下一次DMA传输开始,这个DMA 地址和字节长度是由其它主控决定
4、处理PCI异常,总线上难免会遇到异常情况,从IP提供的状态寄存器可以记录PCI总线上的异常,这个用户逻辑需要处理。可以在本代码上展开完善。
5、保证PCI 时钟以及复位的稳定性,以免遇到状态机跑飞,或者 PCI IP异常情况。
注意申请
c
`timescale 1 ns / 1 ns
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer: felixgao
//
// Create Date:
// Design Name:
// Module Name: pci_master
// Project Name:
// Target Devices:
// Tool Versions: quartus 13.0
// Description:
//
// Dependencies:
//
// Revision:
// date version author
// V0.1 felixgao
// note :
//
// 1. i_lm_tsr[0]为高电平,表示主设备正在请求对PCI总线的控制
// 2. i_lm_tsr[1]为高电平,表示PCI总线被授予
// 3. i_lm_tsr[2]为高电平,说明PCI总线处于地址阶段 此状态下开始传输第一个数据
// 4. i_lm_tsr[3]为0,本地端数据传输模式已完成
// 5. i_lm_tsr[8]为0,本地在上一个时钟周期内PCI总线已完成发生数据传输
// 6. i_lm_tsr[9]为1,向本地端指示目标可以传输32为数据
// 7. i_lm_tsr[4]为高电平,表示延时计数器超时,终止主交易
// 8 .i_lm_tsr[5]为高电平,表示从设备断开连接
//
//////////////////////////////////////////////////////////////////////////////////
module pci_master(
input i_gntn ,
input i_clk ,
input i_rstn ,
input [31:0] i_l_dat ,
input i_lm_adr_ackn ,
input i_lm_ackn ,
input i_lm_dxfrn ,
input [9:0] i_lm_tsr ,
output [31:0] o_l_adi ,
output [3:0] o_cben ,
output o_lm_req32n ,
output o_lm_lastn ,
output o_lm_rdyn ,
// dma message
input i_dma_enable ,
input [31:0] i_dma_addr ,
input [1:0] i_dma_ctrl ,
input [31:0] i_dma_len ,
input i_dma_int_clear ,
input i_dma_int_enable ,
output o_dma_int ,
output o_dma_flag_clear ,
input [10:0] i_fifo_rdcount ,
input i_fifo_empty ,
input [31:0] i_fifo_data ,
output o_fifo_rden ,
output o_switch_m_or_t ,
input i_conv_done ,
input i_io_read_state ,
input i_start_scan ,
input i_stop_scan
);
parameter P_DMA_ONCE_TRANS_NUM = 1; // data count 定义传输个数,指的是FIFO中的读取的个数
parameter P_DMA_ONCE_TRANS_NUM_BYTE = P_DMA_ONCE_TRANS_NUM * 4; // data byte 定义传输的字节数,FIFO一个数据有2个有效字节
parameter P_BUS_DELAY = 20 ;// 一次获取总线传输数据完之后间隔时间控制
parameter S_ERROR = 6'b000000 ,
S_IDLE = 6'b000001 , //Idle
S_WAIT = 6'b000010 ,
S_REQ_BUS = 6'b000100 , //Request Bus
S_WR_ADDR = 6'b001000 , //Write Address phase
S_WR_CHK = 6'b010000 , //Write Check phase
S_WR_DATA = 6'b100000 ; //Write Data phase
wire w_trans_done ;
wire w_trans_error ;
wire w_dxfrn_rise ;
wire w_data_trans_success ;
wire w_lm_rdyn_rise ;
reg [31:0] r_trans_cnt ;
reg [31:0] r_dma_trans_cnt ;
reg [5:0] r_cu_state ;
reg [5:0] r_nx_state ;
reg [7:0] r_sta_cnt ;
reg ri_lm_dxfrn ;
reg r_done ;
reg [31:0] r_dma_addr ;
reg r_dma_write ;
reg ro_lm_rdyn ;
reg ro_lm_lastn ;
reg [31:0] ro_l_adi ;
reg [3:0] ro_cben_reg ;
reg r_fifo_rden ;
reg ro_dma_int ;
reg [31:0] r_local_dma_st_addr ;
reg [10:0] ri_fifo_rdcount ;
reg [31:0] r_dma_trance_diff ;
reg ro_lm_rdyn_1d ;
reg ro_lm_req32n ;
reg r_rdfifo_flag ;
reg r_diff_flag ;
reg ro_dma_flag_clear ;
reg ro_switch_m_or_t ;
reg [9:0] ri_lm_tsr ;
reg [7:0] r_pci_trans_delay ;
reg r_dma_stop ;
reg ro_lm_req32n_1d ;
assign o_lm_req32n = ro_lm_req32n_1d; // 请求pci总线主权,手册时序上保持一个周期,i_lm_tsr[0]为高电平,表示主设备正在请求对PCI总线的控制
assign w_trans_done = (~(i_lm_tsr[2] | i_lm_tsr[3] | i_lm_tsr[8]| i_lm_tsr[9])) && r_done;
assign w_trans_error = (i_lm_tsr[4] | i_lm_tsr[5] | i_lm_tsr[6] | i_lm_tsr[7]);
assign w_dxfrn_rise = i_lm_dxfrn && !ri_lm_dxfrn; // 获取 lm_dxfrn 的上升沿
assign w_data_trans_success = !i_lm_ackn && !i_lm_dxfrn ; // 一次数据传输成功
assign o_lm_rdyn = ro_lm_rdyn ; // 主模式准备好数据
assign o_lm_lastn = ro_lm_lastn; // 主模式结束信号,结束本次主模式传输,也表示本地端最后一个数据阶段
assign o_l_adi = ro_l_adi ; // 主模式传输的 数据和地址
assign o_cben = ro_cben_reg; // 主模式的总线命令,或者 字节使能
assign o_fifo_rden = r_fifo_rden; // DMA FIFO 读使能控制
assign o_dma_int = ro_dma_int ; //
assign w_lm_rdyn_rise = ro_lm_rdyn && !ro_lm_rdyn_1d; //
assign o_dma_flag_clear = ro_dma_flag_clear ; //
assign o_switch_m_or_t = ro_switch_m_or_t ;
assign o_dmaint_timeout_flag = r_dmaint_timeout_flag;
assign o_abnormal_dma_int_cnt = r_abnormal_dma_int_cnt ;
//一次传输的总线延时计数器
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
r_pci_trans_delay <= 0;
else if (r_pci_trans_delay == P_BUS_DELAY)
r_pci_trans_delay <= 0;
else if (r_cu_state == S_REQ_BUS)
r_pci_trans_delay <= r_pci_trans_delay + 1;
else
r_pci_trans_delay <= r_pci_trans_delay;
end
//总线延时
reg r_pci_en_flag;
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
r_pci_en_flag <= 0;
else if (r_pci_trans_delay == P_BUS_DELAY - 1)
r_pci_en_flag <= 1;
else
r_pci_en_flag <= 0;
end
// 当送数据到pci总线时,需要有个切换 master 和 target 信号
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
ro_switch_m_or_t <= 0;
else if (r_cu_state == S_WAIT || (r_cu_state == S_REQ_BUS && r_sta_cnt == 0))
ro_switch_m_or_t <= 0;
else if (i_io_read_state && r_cu_state == S_WR_ADDR) // 解决IO读和 DMA传输冲突关键信号条件
ro_switch_m_or_t <= 0;
else if (!i_io_read_state && r_cu_state == S_WR_ADDR)
ro_switch_m_or_t <= 1;
else if (!ro_lm_req32n)
ro_switch_m_or_t <= 1;
else
ro_switch_m_or_t <= ro_switch_m_or_t;
end
// dma 传输使能清除信号,给到 reg_process模块
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
ro_dma_flag_clear <= 0;
else if (r_cu_state == S_WAIT && i_dma_enable && r_dma_write)
ro_dma_flag_clear <= 1;
else
ro_dma_flag_clear <= 0;
end
// 可以从 FIFO中读出数据的标志,当FIFO里的数据 等于设定的值
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
r_rdfifo_flag <= 0;
else if (ri_fifo_rdcount >= P_DMA_ONCE_TRANS_NUM)
r_rdfifo_flag <= 1;
else
r_rdfifo_flag <= 0;
end
// 请求 PCI总线主权,拉低一个周期
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
ro_lm_req32n <= 1;
else if (i_stop_scan)
ro_lm_req32n <= 1;
else if (r_cu_state == S_REQ_BUS && r_rdfifo_flag && r_pci_en_flag)
ro_lm_req32n <= 0;
else
ro_lm_req32n <= 1;
end
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
ro_lm_req32n_1d <= 1;
else
ro_lm_req32n_1d <= ro_lm_req32n;
end
//控制 DMA FIFO的读使能
always @ (*)
begin
if (!i_gntn && !i_lm_adr_ackn && (i_lm_tsr == 2 || i_lm_tsr == 6))
r_fifo_rden = 1;
else
r_fifo_rden = 0;
end
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn) begin
ri_lm_dxfrn <= 1;
ri_fifo_rdcount <= 0;
ro_lm_rdyn_1d <= 1;
ri_lm_tsr <= 0;
end else begin
ri_lm_dxfrn <= i_lm_dxfrn;
ri_fifo_rdcount <= i_fifo_rdcount;
ro_lm_rdyn_1d <= ro_lm_rdyn ;
ri_lm_tsr <= i_lm_tsr;
end
end
// 确保传输完成
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
r_done <= 0;
else if (r_cu_state == S_REQ_BUS)
r_done <= 0;
else if (r_cu_state == S_WR_CHK)
r_done <= 1;
else
r_done <= r_done;
end
// once dma trans bytecnt 一次DMA传输,记录字节个数
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
r_trans_cnt <= 0;
else if (r_cu_state == S_REQ_BUS)
r_trans_cnt <= 0;
else if (w_data_trans_success)
r_trans_cnt <= r_trans_cnt + 4;
else
r_trans_cnt <= r_trans_cnt;
end
// 多次DMA传输,记录字节总个数
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
r_dma_trans_cnt <= 0;
else if (r_cu_state == S_WAIT && r_nx_state == S_REQ_BUS)
r_dma_trans_cnt <= 0;
else if (r_cu_state == S_WR_CHK && r_sta_cnt == 0)
r_dma_trans_cnt <= r_dma_trans_cnt + 4;
else
r_dma_trans_cnt <= r_dma_trans_cnt;
end
//记录 当前DMA传输的 剩余传输字节数
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
r_dma_trance_diff <= 0;
else if (r_cu_state == S_REQ_BUS && r_sta_cnt == 0)
r_dma_trance_diff <= i_dma_len - r_dma_trans_cnt;
else
r_dma_trance_diff <= r_dma_trance_diff;
end
// 主模式 三段式状态机
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
r_cu_state <= S_IDLE;
else
r_cu_state <= r_nx_state;
end
always @ (*)
begin
case(r_cu_state)
S_IDLE : begin // 空闲状态等待10周期,状态跳转
r_nx_state = S_WAIT;
end
S_WAIT : begin // DMA 使能,状态跳转
if (i_dma_enable && r_dma_write)
r_nx_state = S_REQ_BUS;
else
r_nx_state = r_cu_state;
end
S_REQ_BUS : begin // 当DMA FIFO 有一定数据 ,DMA 写传输时,状态跳转
if (i_stop_scan)
r_nx_state = S_IDLE;
else if (r_rdfifo_flag && r_pci_en_flag)
r_nx_state = S_WR_ADDR;
else
r_nx_state = r_cu_state;
end
S_WR_ADDR : begin
if (!i_gntn && !i_lm_adr_ackn && (i_lm_tsr == 2 || i_lm_tsr == 6))
r_nx_state = S_WR_CHK;
else if (r_sta_cnt == 63)
r_nx_state = S_REQ_BUS;
else
r_nx_state = r_cu_state;
end
S_WR_CHK : begin
if ((w_data_trans_success || w_trans_error) && i_dma_len == r_dma_trans_cnt + 4)
r_nx_state = S_WAIT;
else if ((w_data_trans_success || w_trans_error) && i_dma_len != r_dma_trans_cnt + 4)
r_nx_state = S_WR_DATA;
else if (r_sta_cnt == 63)
r_nx_state = S_REQ_BUS;
else
r_nx_state = r_cu_state;
end
S_WR_DATA : begin //传输数据阶段,判断工控机要求的DMA字节数 是否和本地传输字节数据相等,相等,结束DMA传输,等待下一轮DMA开始;不相等,进行下一次DMA传输
if ((w_trans_done || w_trans_error) && i_dma_len == r_dma_trans_cnt)
r_nx_state = S_WAIT;
else if ((w_trans_done || w_trans_error) && i_dma_len != r_dma_trans_cnt)
r_nx_state = S_REQ_BUS;
else if (r_sta_cnt == 63) // pci 总线异常情况,回到 DMA 初始状态,等待新的数据传输
r_nx_state = S_REQ_BUS;
else
r_nx_state = r_cu_state;
end
S_ERROR : begin
if (r_sta_cnt == 31) //状态机异常
r_nx_state = S_REQ_BUS;
else
r_nx_state = r_cu_state;
end
default : r_nx_state = S_ERROR;
endcase
end
//状态计数器
always @ (posedge i_clk or negedge i_rstn)
begin
if (!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;
end
//pci总线异常情况
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
r_pci_abnormal_flag <= 0;
else if (i_dma_enable)
r_pci_abnormal_flag <= 0;
else if ((r_cu_state == S_WR_ADDR || r_cu_state == S_WR_CHK || r_cu_state == S_WR_DATA ) && (r_sta_cnt == 63))
r_pci_abnormal_flag <= 1;
else
r_pci_abnormal_flag <= r_pci_abnormal_flag;
end
//本地地址累加记录
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
r_local_dma_st_addr <= 0;
else if (r_cu_state == S_WAIT && r_nx_state == S_REQ_BUS)
r_local_dma_st_addr <= i_dma_addr;
else if (r_cu_state == S_WR_CHK && r_sta_cnt == 0 && r_dma_write)
r_local_dma_st_addr <= r_local_dma_st_addr + 4;
else
r_local_dma_st_addr <= r_local_dma_st_addr;
end
// DMA写指令
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
r_dma_write <= 0;
else if (r_dma_stop)
r_dma_write <= 0;
else if (i_dma_ctrl == 2'b10)
r_dma_write <= 1;
else
r_dma_write <= r_dma_write;
end
//DMA 停止传输标志信号
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
r_dma_stop <= 0;
else if (i_dma_ctrl != 2'b00)
r_dma_stop <= 0;
else if (i_dma_ctrl == 2'b00)
r_dma_stop <= 1;
else
r_dma_stop <= r_dma_stop;
end
//DMA中断,当DMA传输字节数量 和 工控机要求传输字节数量相等,拉起一次中断
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
ro_dma_int <= 1;
else if (!i_dma_int_clear)
ro_dma_int <= 1;
else if (i_dma_int_enable && w_trans_done && i_dma_len == r_dma_trans_cnt )
ro_dma_int <= 0;
else
ro_dma_int <= ro_dma_int;
end
// 当剩余一次DMA传输字节数 不满足一次传输的字节个数的标志信号
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
r_diff_flag <= 0;
else if (r_dma_trance_diff < P_DMA_ONCE_TRANS_NUM_BYTE)
r_diff_flag <= 1;
else
r_diff_flag <= 0;
end
// 优化逻辑
always @ (*)
begin
if (!i_rstn)
ro_lm_rdyn = 1;
else if (r_cu_state == S_WR_CHK && i_lm_dxfrn)
ro_lm_rdyn = 0;
else if (!i_gntn && !i_lm_adr_ackn && (i_lm_tsr == 2 || i_lm_tsr == 6))
ro_lm_rdyn = 0;
else
ro_lm_rdyn = 1;
end
//主模式结束信号
//assign ro_lm_lastn = !i_lm_ackn && !i_lm_dxfrn ;
always @ (*)
begin
ro_lm_lastn = ~(!i_lm_ackn && !i_lm_dxfrn) ;
end
// 主设备 读写命令 或者 字节使能
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
ro_cben_reg <= 0;
else if (!i_gntn && !i_lm_adr_ackn && (i_lm_tsr == 2 || i_lm_tsr == 6))
ro_cben_reg <= 0;
else if (r_dma_write && r_cu_state == S_WR_ADDR)
ro_cben_reg <= 7;
else
ro_cben_reg <= 0;
end
//主设备 DMA 写的数据,初始地址、累计地址、传输的FIFO的数据
always @ (posedge i_clk or negedge i_rstn)
begin
if (!i_rstn)
ro_l_adi <= 0;
else if (r_cu_state == S_WAIT)
ro_l_adi <= i_dma_addr;
else if (r_cu_state == S_REQ_BUS)
ro_l_adi <= r_local_dma_st_addr;
else if (r_fifo_rden)
ro_l_adi <= i_fifo_data;
else
ro_l_adi <= ro_l_adi;
end
endmodule