Altera Fpga PCI master 设计

本文分享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
相关推荐
爱吃汽的小橘1 天前
异步串口通信和逻辑分析仪
运维·服务器·网络·单片机·嵌入式硬件·fpga开发
bnsarocket1 天前
Verilog和FPGA的自学笔记3——仿真文件Testbench的编写
笔记·fpga开发·verilog·自学
Eloudy2 天前
Verilog可综合电路设计:重要语法细节指南
fpga开发
ARM+FPGA+AI工业主板定制专家2 天前
基于ZYNQ FPGA+AI+ARM 的卷积神经网络加速器设计
人工智能·fpga开发·cnn·无人机·rk3588
szxinmai主板定制专家2 天前
基于 ZYNQ ARM+FPGA+AI YOLOV4 的电网悬垂绝缘子缺陷检测系统的研究
arm开发·人工智能·嵌入式硬件·yolo·fpga开发
ooo-p2 天前
FPGA学习篇——Verilog学习之计数器的实现
学习·fpga开发
bnsarocket3 天前
Verilog和FPGA的自学笔记1——FPGA
笔记·fpga开发·verilog·自学
最遥远的瞬间3 天前
一、通用的FPGA开发流程介绍
fpga开发
weixin_450907283 天前
第八章 FPGA 片内 FIFO 读写测试实验
fpga开发