下面我们来看看PIO的状态转移
状态转移

首先进入PIO_RX_RST_START 状态,这是一个复位状态,在复位状态中,首先判断sop 信号是否有效,sop 信号是TLP开始的信号,若这个信号无效,则程序会一直在复位状态中直到sop 信号有效
case (state)
若在复位状态,则将rx_tready信号置1表示IP核已准备好接收接口传过来的TLP包
PIO_RX_RST_STATE : begin

          m_axis_rx_tready <= #TCQ 1'b1;
          req_compl_wd     <= #TCQ 1'b1;

//若检测到帧开始标志,则准备检测属于什么类型的包
          if (sop)
          begin
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

m_axis_rx_tdata的[30:24]位表示事务类型,为存储器读3DW不带数据请求时,将其赋值给tlp_type,此时的低10位赋值给req_len(有效负载大小)也就是现在已知帧类型,有效负载大小, m_axis_rx_tdata[9:0] 是代表当前TLP的Length,这个Length表示这个TLP中数据的数量,单位是DW(64bit),m_axis_rx_tdata[9:0]==10’b1这是因为PIO通常一次传输一个DW数据,判断m_axis_rx_tdata[9:0]==10’b1,确定当前是PIO模式,接着解析出TLP中的其他字段信息,然后跳转到PIO_RX_MEM_RD32_DW1DW2 状态。

            case (m_axis_rx_tdata[30:24])
	//若检测到帧类型为存储器32位读请求
              PIO_RX_MEM_RD32_FMT_TYPE : begin

                tlp_type     <= #TCQ m_axis_rx_tdata[31:24];
                req_len      <= #TCQ m_axis_rx_tdata[9:0];
                m_axis_rx_tready <= #TCQ 1'b0;

//根据TLP包的特性进行组包,
//当有效负载为1时。分别将m_axis_rx_tdata的[22:20]位赋值为TC事务优先级,
//15位赋值为TD(TLP Digest是否有效高有效),14位赋值为EP(数据是否有效低有效),
// 13:12赋值为attr(传输模式选择),9:0赋值为len,63:48赋值为BDF号,47:40赋值为标签类型,39:32赋值为字节使能
//状态转为PIO_RX_MEM_RD32_DW1DW2否则转为复位状态
                if (m_axis_rx_tdata[9:0] == 10'b1)///TLP长度为lenth为1代表需要读取的数据长度为1,xapp1052中由RC发起的存储器读写均为单次的读写,即length为1。只带一个数据。
                begin

                  req_tc     <= #TCQ m_axis_rx_tdata[22:20];
                  req_td     <= #TCQ m_axis_rx_tdata[15];
                  req_ep     <= #TCQ m_axis_rx_tdata[14];
                  req_attr   <= #TCQ m_axis_rx_tdata[13:12];
                  req_len    <= #TCQ m_axis_rx_tdata[9:0];
                  req_rid    <= #TCQ m_axis_rx_tdata[63:48];
                  req_tag    <= #TCQ m_axis_rx_tdata[47:40];
                  req_be     <= #TCQ m_axis_rx_tdata[39:32];
                  state      <= #TCQ PIO_RX_MEM_RD32_DW1DW2;        //检测到存储器32位读类型则进入存储器32位读状态

                end // if (m_axis_rx_tdata[9:0] == 10'b1)
                else		//
                begin

                  state        <= #TCQ PIO_RX_RST_STATE;

                end // if !(m_axis_rx_tdata[9:0] == 10'b1)

              end // PIO_RX_MEM_RD32_FMT_TYPE
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

//若m_axis_rx_tdata[31:24]检测出写请求带数据,状态转为存储器写请求。 首先是从TLP中解析出TLP类型和这个TLP中数据的长度(单位是DW);然后同样将ready信号置0,表明还没有准备好接收下一个TLP;接着判定这个TLP中的数据长度是否为1DW(因为这是PIO模式,所以数据长度为1DW);最后解析出FIRST DW BE和LAST DW BE,判断传输过来的数据是否有效;状态跳转到PIO_RX_MEM_WR32_DW1DW2。

              PIO_RX_MEM_WR32_FMT_TYPE : begin

                tlp_type     <= #TCQ m_axis_rx_tdata[31:24];
                req_len      <= #TCQ m_axis_rx_tdata[9:0];
                m_axis_rx_tready <= #TCQ 1'b0;		//未准备好接收下一个

                if (m_axis_rx_tdata[9:0] == 10'b1)///长度为1,写一个字节
                begin

                  wr_be      <= #TCQ m_axis_rx_tdata[39:32];    //写字节使能赋值,并将状态转为存储器32位写状态
                  state      <= #TCQ PIO_RX_MEM_WR32_DW1DW2;

                end // if (m_axis_rx_tdata[9:0] == 10'b1)
                else
                begin

                  state      <= #TCQ PIO_RX_RST_STATE;

                end // if !(m_axis_rx_tdata[9:0] == 10'b1)

              end // PIO_RX_MEM_WR32_FMT_TYPE
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

//数据类型为4DW存储器读请求,不带数据

              PIO_RX_MEM_RD64_FMT_TYPE : begin

                tlp_type     <= #TCQ m_axis_rx_tdata[31:24];
                req_len      <= #TCQ m_axis_rx_tdata[9:0];
                m_axis_rx_tready <= #TCQ 1'b0;

                if (m_axis_rx_tdata[9:0] == 10'b1)//代表读取的是一个字节
                begin

                  req_tc     <= #TCQ m_axis_rx_tdata[22:20];
                  req_td     <= #TCQ m_axis_rx_tdata[15];
                  req_ep     <= #TCQ m_axis_rx_tdata[14];
                  req_attr   <= #TCQ m_axis_rx_tdata[13:12];
                  req_len    <= #TCQ m_axis_rx_tdata[9:0];
                  req_rid    <= #TCQ m_axis_rx_tdata[63:48];
                  req_tag    <= #TCQ m_axis_rx_tdata[47:40];
                  req_be     <= #TCQ m_axis_rx_tdata[39:32];
                  state        <= #TCQ PIO_RX_MEM_RD64_DW1DW2;          ///状态变为存储器64位读

                end // if (m_axis_rx_tdata[9:0] == 10'b1)
                else
                begin

                  state      <= #TCQ PIO_RX_RST_STATE;

                end // if !(m_axis_rx_tdata[9:0] == 10'b1)

              end // PIO_RX_MEM_RD64_FMT_TYPE
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

//检测到帧类型为存储器写4DW

              PIO_RX_MEM_WR64_FMT_TYPE : begin

                tlp_type     <= #TCQ m_axis_rx_tdata[31:24];
                req_len      <= #TCQ m_axis_rx_tdata[9:0];

                if (m_axis_rx_tdata[9:0] == 10'b1) begin

                  wr_be      <= #TCQ m_axis_rx_tdata[39:32];        ///字节使能并将状态转为存储器64位写状态
                  state      <= #TCQ PIO_RX_MEM_WR64_DW1DW2;

                end // if (m_axis_rx_tdata[9:0] == 10'b1)
                else
                begin

                  state      <= #TCQ PIO_RX_RST_STATE;

                end // if !(m_axis_rx_tdata[9:0] == 10'b1)

              end // PIO_RX_MEM_WR64_FMT_TYPE
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

//若检测到帧类型为IO读请求3DW

              PIO_RX_IO_RD32_FMT_TYPE : begin

                tlp_type     <= #TCQ m_axis_rx_tdata[31:24];
                req_len      <= #TCQ m_axis_rx_tdata[9:0];
                m_axis_rx_tready <= #TCQ 1'b0;


                if (m_axis_rx_tdata[9:0] == 10'b1)
                begin

                  req_tc     <= #TCQ m_axis_rx_tdata[22:20];
                  req_td     <= #TCQ m_axis_rx_tdata[15];
                  req_ep     <= #TCQ m_axis_rx_tdata[14];
                  req_attr   <= #TCQ m_axis_rx_tdata[13:12];
                  req_len    <= #TCQ m_axis_rx_tdata[9:0];
                  req_rid    <= #TCQ m_axis_rx_tdata[63:48];
                  req_tag    <= #TCQ m_axis_rx_tdata[47:40];
                  req_be     <= #TCQ m_axis_rx_tdata[39:32];
                  state      <= #TCQ PIO_RX_MEM_RD32_DW1DW2;        ///状态变为存储器读32位数据

                end // if (m_axis_rx_tdata[9:0] == 10'b1)
                else
                begin

                  state      <= #TCQ PIO_RX_RST_STATE;

                end // if !(m_axis_rx_tdata[9:0] == 10'b1)

              end // PIO_RX_IO_RD32_FMT_TYPE
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

//检测到帧类型为IO写数据3DW

              PIO_RX_IO_WR32_FMT_TYPE : begin

                tlp_type     <= #TCQ m_axis_rx_tdata[31:24];
                req_len      <= #TCQ m_axis_rx_tdata[9:0];
                m_axis_rx_tready <= #TCQ 1'b0;

                if (m_axis_rx_tdata[9:0] == 10'b1)
                begin

                  req_tc     <= #TCQ m_axis_rx_tdata[22:20];
                  req_td     <= #TCQ m_axis_rx_tdata[15];
                  req_ep     <= #TCQ m_axis_rx_tdata[14];
                  req_attr   <= #TCQ m_axis_rx_tdata[13:12];
                  req_len    <= #TCQ m_axis_rx_tdata[9:0];
                  req_rid    <= #TCQ m_axis_rx_tdata[63:48];
                  req_tag    <= #TCQ m_axis_rx_tdata[47:40];
                  req_be     <= #TCQ m_axis_rx_tdata[39:32];
                  wr_be      <= #TCQ m_axis_rx_tdata[39:32];
                  state      <= #TCQ PIO_RX_IO_WR_DW1DW2;           ///转变为IO写状态

                end //if (m_axis_rx_tdata[9:0] == 10'b1)
                else
                begin

                  state        <= #TCQ PIO_RX_RST_STATE;

                end //if !(m_axis_rx_tdata[9:0] == 10'b1)

              end // PIO_RX_IO_WR32_FMT_TYPE


              default : begin // other TLPs

                state        <= #TCQ PIO_RX_RST_STATE;

              end // default

            endcase

          end // if (sop)
          else
              state <= #TCQ PIO_RX_RST_STATE;

        end // PIO_RX_RST_STATE
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">
存储器读32位数据状态

存储器读请求是需要反馈完成报文的,而且这个完成报文反馈的是从存储器指定地址下的数据,
在PIO_RX_MEM_RD32_DW1DW2状态中的req_compl 、req_compl_wd 和req_addr 三个信号,其中req_addr 信号输入到存储器模块中读取该地址下数据,然后又将数据发送到发送引擎中。req_compl和req_compl_wd这两个信号输入到发送引擎中用以合成完成报文,并在完成报文发送之后,发送引擎产生compl_done 信号,表明完成包已经发送完成,该信号输入到接收引擎中,这时表明一个存储器读请求已经完成,再在PIO_RX_WAIT_STATE 状态中将m_axis_rx_tready信号置1,表明接收引擎准备好可以接收下一个TLP了,状态又跳转到PIO_RX_RST_STATE状态。

        PIO_RX_MEM_RD32_DW1DW2 : begin

          if (m_axis_rx_tvalid)
          begin

            m_axis_rx_tready <= #TCQ 1'b0;
            req_addr     <= #TCQ {region_select[1:0],m_axis_rx_tdata[10:2], 2'b00};     ///从接收到的32位数据中提取出9位地址(通过位选择[10:2]),并且通过region_select[1:0]来附加额外的区域选择信息(这通常是为了区分不同的内存区域)。2'b00可能是为了对齐地址,确保地址是字对齐的。
            req_compl    <= #TCQ 1'b1;
            req_compl_wd <= #TCQ 1'b1;                                                  ///请求完成,用于事务的后续处理
            state        <= #TCQ PIO_RX_WAIT_STATE;                                     ///进入等待状态

          end // if (m_axis_rx_tvalid)
          else
            state        <= #TCQ PIO_RX_MEM_RD32_DW1DW2;                                ///若未准备好有效数据则将状态转回存储器读32位数据状态,重新进行操作

        end // PIO_RX_MEM_RD32_DW1DW2
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">
存储器写32位状态。

首先通过m_axis_rx_tdata[63:32]解析出这个TLP中的数据;然后设置wr_en有效,这个信号是使能存储器写功能;接着设置m_axis_rx_tready无效,表明还没准备好接收下一个包;最后解析出需要写入存储器的地址wr_addr,状态跳转到PIO_RX_WAIT_STATE。

        PIO_RX_MEM_WR32_DW1DW2 : begin

          if (m_axis_rx_tvalid)
          begin

            wr_data      <= #TCQ m_axis_rx_tdata[63:32];                            //需要写入数据为m_axis_rx_tdata[63:32]上的数据
            wr_en        <= #TCQ 1'b1;
            m_axis_rx_tready <= #TCQ 1'b0;
            wr_addr      <= #TCQ {region_select[1:0],m_axis_rx_tdata[10:2]};        //写的地址为从接收到的32位数据中提取出9位地址(通过位选择[10:2]),并且通过region_select[1:0]来附加额外的区域选择信息(这通常是为了区分不同的内存区域)
            state        <= #TCQ  PIO_RX_WAIT_STATE;

          end // if (m_axis_rx_tvalid)
          else
            state        <= #TCQ PIO_RX_MEM_WR32_DW1DW2;                            //若未准备好有效数据则将状态转回重新进行操作

        end // PIO_RX_MEM_WR32_DW1DW2
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

存储器读64位数据状态

        PIO_RX_MEM_RD64_DW1DW2 : begin

          if (m_axis_rx_tvalid)
          begin

            req_addr     <= #TCQ {region_select[1:0],m_axis_rx_tdata[42:34], 2'b00};    //从接收到的64位数据中提取出9位地址(通过位选择[42:34]),并且通过region_select[1:0]来附加额外的区域选择信息(这通常是为了区分不同的内存区域)。2'b00可能是为了对齐地址,确保地址是字对齐的。
            req_compl    <= #TCQ 1'b1;
            req_compl_wd <= #TCQ 1'b1;
            m_axis_rx_tready <= #TCQ 1'b0;
            state        <= #TCQ PIO_RX_WAIT_STATE;

          end // if (m_axis_rx_tvalid)
          else
            state        <= #TCQ PIO_RX_MEM_RD64_DW1DW2;

        end // PIO_RX_MEM_RD64_DW1DW2
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

存储器写64位状态

        PIO_RX_MEM_WR64_DW1DW2 : begin

          if (m_axis_rx_tvalid)
          begin

            m_axis_rx_tready <= #TCQ 1'b0;
            wr_addr        <= #TCQ {region_select[1:0],m_axis_rx_tdata[42:34]};
            state          <= #TCQ  PIO_RX_MEM_WR64_DW3;                            //状态转为准备接收数据的下一个 32 位部分(DW3),否则停留状态等待数据有效

          end // if (m_axis_rx_tvalid)
          else
            state          <= #TCQ PIO_RX_MEM_WR64_DW1DW2;

        end // PIO_RX_MEM_WR64_DW1DW2


        PIO_RX_MEM_WR64_DW3 : begin

          if (m_axis_rx_tvalid)
          begin

            wr_data      <= #TCQ m_axis_rx_tdata[31:0];             ///接下来的32位数据均为写入数据
            wr_en        <= #TCQ 1'b1;
            m_axis_rx_tready <= #TCQ 1'b0;
            state        <= #TCQ PIO_RX_WAIT_STATE;

          end // if (m_axis_rx_tvalid)
          else
             state        <= #TCQ PIO_RX_MEM_WR64_DW3;

        end // PIO_RX_MEM_WR64_DW3
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

//

        PIO_RX_IO_WR_DW1DW2 : begin

          if (m_axis_rx_tvalid)
          begin

            wr_data         <= #TCQ m_axis_rx_tdata[63:32];
            wr_en           <= #TCQ 1'b1;
            m_axis_rx_tready  <= #TCQ 1'b0;
            wr_addr         <= #TCQ {region_select[1:0],m_axis_rx_tdata[10:2]};
            req_compl       <= #TCQ 1'b1;
            req_compl_wd    <= #TCQ 1'b0;
            state             <= #TCQ  PIO_RX_WAIT_STATE;

          end // if (m_axis_rx_tvalid)
          else
            state             <= #TCQ PIO_RX_IO_WR_DW1DW2;
        end // PIO_RX_IO_WR_DW1DW2
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">
接收端的等待状态

检查是否接收到有效的请求,并进行响应(当检测到接收到且不处在busy状态时,tready信号拉高代表已准备好接收,状态随之返回重置状态)

若为写请求,判定TLP的类型和wr_busy 信号。wr_busy 信号是一个写繁忙信号,因为这个TLP是想往存储器中写数据,当要写数据送入存储器模块之后,存储器模块产生一个写繁忙有效信号表明此时在写存储器,当写接收之后,写繁忙信号失效,这时在接收引擎中接收到wr_busy 信号失效,表明存储器写请求处理完成,所以设置ready信号有效,表明可以接收下一个TLP了,状态也跳转回复位状态。

若为读请求,在该状态下,程序先通过tlp_type 和compl_done 两个信号为条件进行判定。tlp_type 表示当前TLP的类型,compl_done 表示完成包已经发送完成,由发送引擎反馈给接收引擎。在PIO_RX_MEM_RD32_DW1DW2状态中的req_compl 、req_compl_wd 和req_addr 三个信号,其中req_addr 信号输入到存储器模块中读取该地址下数据,然后又将数据发送到发送引擎中。req_compl和req_compl_wd这两个信号输入到发送引擎中用以合成完成报文,当合成完成报文并发送之后,发送引擎产生compl_done 信号,表明完成包已经发送完成,该信号输入到接收引擎中,这时表明一个存储器读请求已经完成,所以在PIO_RX_WAIT_STATE 状态中将m_axis_rx_tready信号置1,表明接收引擎准备好可以接收下一个TLP了,状态又跳转到PIO_RX_RST_STATE状态。

        PIO_RX_WAIT_STATE : begin

          wr_en      <= #TCQ 1'b0;
          req_compl  <= #TCQ 1'b0;

          if ((tlp_type == PIO_RX_MEM_WR32_FMT_TYPE) && (!wr_busy))
          begin

            m_axis_rx_tready <= #TCQ 1'b1;
            state        <= #TCQ PIO_RX_RST_STATE;

          end // if ((tlp_type == PIO_RX_MEM_WR32_FMT_TYPE) && (!wr_busy))
          else if ((tlp_type == PIO_RX_IO_WR32_FMT_TYPE) && (!wr_busy))
          begin

            m_axis_rx_tready <= #TCQ 1'b1;
            state        <= #TCQ PIO_RX_RST_STATE;

          end // if ((tlp_type == PIO_RX_IO_WR32_FMT_TYPE) && (!wr_busy))
          else if ((tlp_type == PIO_RX_MEM_WR64_FMT_TYPE) && (!wr_busy))
          begin

            m_axis_rx_tready <= #TCQ 1'b1;
            state        <= #TCQ PIO_RX_RST_STATE;

          end // if ((tlp_type == PIO_RX_MEM_WR64_FMT_TYPE) && (!wr_busy))
          else if ((tlp_type == PIO_RX_MEM_RD32_FMT_TYPE) && (compl_done))
          begin

            m_axis_rx_tready <= #TCQ 1'b1;
            state        <= #TCQ PIO_RX_RST_STATE;

          end // if ((tlp_type == PIO_RX_MEM_RD32_FMT_TYPE) && (compl_done))
          else if ((tlp_type == PIO_RX_IO_RD32_FMT_TYPE) && (compl_done))
          begin

            m_axis_rx_tready <= #TCQ 1'b1;
            state        <= #TCQ PIO_RX_RST_STATE;

          end // if ((tlp_type == PIO_RX_IO_RD32_FMT_TYPE) && (compl_done))
          else if ((tlp_type == PIO_RX_MEM_RD64_FMT_TYPE) && (compl_done))
          begin

            m_axis_rx_tready <= #TCQ 1'b1;
            state        <= #TCQ PIO_RX_RST_STATE;

          end // if ((tlp_type == PIO_RX_MEM_RD64_FMT_TYPE) && (compl_done))
          else
            state        <= #TCQ PIO_RX_WAIT_STATE;

        end // PIO_RX_WAIT_STATE

        default : begin
          // default case stmt
          state        <= #TCQ PIO_RX_RST_STATE;
        end // default

      endcase
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

TX_ENGINE

还是先看输入输出部分,和接收引擎基本类似,几个不一样的信号也添加了注释

	module PIO_TX_ENGINE    #(
	// RX/TX interface data width
	parameter C_DATA_WIDTH = 64,
	parameter TCQ = 1,

	// TSTRB width
	parameter KEEP_WIDTH = C_DATA_WIDTH / 8
	)(

	input             clk,
	input             rst_n,

	// AXIS
	input                           s_axis_tx_tready,
	output  reg [C_DATA_WIDTH-1:0]  s_axis_tx_tdata,
	output  reg [KEEP_WIDTH-1:0]    s_axis_tx_tkeep,
	output  reg                     s_axis_tx_tlast,
	output  reg                     s_axis_tx_tvalid,
	output                          tx_src_dsc,			//停止传输

	input                           req_compl,			//req_compl和req_compl_wd这两个信号输入到发送引擎中用以合成完成报文,并在完成报文发送完成后,产生compl_done信号
	input                           req_compl_wd,
	output reg                      compl_done,			//compl_done 表示完成包已经发送完成,由发送引擎反馈给接收引擎
	//完成包格式的信号,有一个地址和字节使能
	input [2:0]                     req_tc,
	input                           req_td,
	input                           req_ep,
	input [1:0]                     req_attr,
	input [9:0]                     req_len,
	input [15:0]                    req_rid,
	input [7:0]                     req_tag,
	input [7:0]                     req_be,
	input [12:0]                    req_addr,
	//发送引擎控制,接收到数据和ID并发送地址及使能字节
	output [10:0]                   rd_addr,	
	output reg [3:0]                rd_be,
	input  [31:0]                   rd_data,
	input [15:0]                    completer_id

	);
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

接下来发送引擎的状态机部分

localparam PIO_CPLD_FMT_TYPE      = 7'b10_01010;		//带数据的完成包
localparam PIO_CPL_FMT_TYPE       = 7'b00_01010;		//不带数据的完成包
localparam PIO_TX_RST_STATE       = 2'b00;				//发送引擎复位状态
localparam PIO_TX_CPLD_QW1_FIRST  = 2'b01;				//
localparam PIO_TX_CPLD_QW1_TEMP   = 2'b10;				//
localparam PIO_TX_CPLD_QW1        = 2'b11;				//
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

一些内部信号

// Local registers

reg [11:0]              byte_count;		//有效字节个数
reg [6:0]               lower_addr;

reg                     req_compl_q;
reg                     req_compl_wd_q;

reg                     compl_busy_i;

// Local wires

wire                    compl_wd;

// Unused discontinue
assign tx_src_dsc = 1'b0;		//不会终止传输

// Present address and byte enable to memory module

assign rd_addr = req_addr[12:2];	//
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

几个重要信号打一拍,主要作用是进行输入同步

//同步输入的字节使能信号req_be到rd_be
always @(posedge clk) begin
if (!rst_n)
begin
 rd_be <= #TCQ 0;
end else begin
 rd_be <= #TCQ req_be[3:0];
end
end

// Calculate byte count based on byte enable根据rd_be(字节使能信号)的值计算有效字节的数量,并将结果存储在byte_count中

always @ (rd_be) begin
	casex (rd_be[3:0])
  	4'b1xx1 : byte_count = 12'h004;
  	4'b01x1 : byte_count = 12'h003;
  	4'b1x10 : byte_count = 12'h003;
  	4'b0011 : byte_count = 12'h002;
  	4'b0110 : byte_count = 12'h002;
  	4'b1100 : byte_count = 12'h002;
  	4'b0001 : byte_count = 12'h001;
  	4'b0010 : byte_count = 12'h001;
  	4'b0100 : byte_count = 12'h001;
  	4'b1000 : byte_count = 12'h001;
  	4'b0000 : byte_count = 12'h001;
	endcase
end
//完成信号打一拍,进行输入同步
always @ ( posedge clk ) begin
	if (!rst_n ) 
	begin
  	req_compl_q      <= #TCQ 1'b0;
  	req_compl_wd_q   <= #TCQ 1'b1;
	end // if !rst_n
	else
	begin
  	req_compl_q      <= #TCQ req_compl;
  	req_compl_wd_q   <= #TCQ req_compl_wd;
	end // if rst_n
end
//这里的lower_addr指的是返回的数据的起始地址,如果不带数据,设置为全0,若携带数据则根据字节使能及请求地址来确定其起始地址。根据字节使能和请求地址计算低位地址,若使能最低位或者均未使能,则低位地址取req_addr,00 ,若使能第二个低字节,01,以此类推
always @ (rd_be or req_addr or compl_wd) begin
casex ({compl_wd, rd_be[3:0]})
   5'b1_0000 : lower_addr = {req_addr[6:2], 2'b00};
   5'b1_xxx1 : lower_addr = {req_addr[6:2], 2'b00};
   5'b1_xx10 : lower_addr = {req_addr[6:2], 2'b01};
   5'b1_x100 : lower_addr = {req_addr[6:2], 2'b10};
   5'b1_1000 : lower_addr = {req_addr[6:2], 2'b11};
   5'b0_xxxx : lower_addr = 8'h0;
endcase // casex ({compl_wd, rd_be[3:0]})
end
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

下面就是重点了,发送引擎的状态转移

compl_done这个信号就是在接收引擎等待状态中的compl_done 信号。接着判定req_compl_q 信号(这个信号就是从接收引擎传来的发送完成包信号),如果需要发送完成包,则设置compl_busy_i 信号有效,表示正在发送完成包,接下来就是状态机跳转发送完成包了

请添加图片描述

首先进入复位状态,在compl_busy_i有效的情况下进行初始化,然后判定从机是否准备好接收信号,接着跳转到PIO_TX_CPLD_QW1_FIRST 状态。

		case ( state )
	
        PIO_TX_RST_STATE : begin

          if (compl_busy_i) 
          begin
            
            s_axis_tx_tdata   <= #TCQ {C_DATA_WIDTH{1'b0}};
            s_axis_tx_tkeep   <= #TCQ 8'hFF;
            s_axis_tx_tlast   <= #TCQ 1'b0;
            s_axis_tx_tvalid  <= #TCQ 1'b0;
              if (s_axis_tx_tready)
                state             <= #TCQ PIO_TX_CPLD_QW1_FIRST;
              else
              state             <= #TCQ PIO_TX_RST_STATE;
           end
          else
          begin

            s_axis_tx_tlast   <= #TCQ 1'b0;
            s_axis_tx_tvalid  <= #TCQ 1'b0;
            s_axis_tx_tdata   <= #TCQ 64'b0;
            s_axis_tx_tkeep   <= #TCQ 8'hFF;
            compl_done        <= #TCQ 1'b0;
            state             <= #TCQ PIO_TX_RST_STATE;

          end // if !(compl_busy) 
          end // PIO_TX_RST_STATE
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

PIO_TX_CPLD_QW1_FIRST状态
这个状态中传输第一帧(64bit)数据,这一帧数据是完成报文的前2DW数据,其中通过req_compl_wd_q 信号判定这个完成包是否带有数据,同时因为这一帧信号都是有效的信号,所以s_axis_tx_tkeep 为FF。接着状态机跳转。

        PIO_TX_CPLD_QW1_FIRST : begin
          if (s_axis_tx_tready) begin

            s_axis_tx_tlast  <= #TCQ 1'b0;
            s_axis_tx_tdata  <= #TCQ {                      // Bits   这部分内容可以对照上一篇中配置包的格式来对应使用
                                  completer_id,             // 16
                                  {3'b0},                   // 3	staus
                                  {1'b0},                   // 1	BCM
                                  byte_count,               // 12,记录源设备还需要从目标设备中拿多少个数据
                                  {1'b0},                   // 1  ,fmt+type,一共8位
                                  (req_compl_wd_q ?
                                  PIO_CPLD_FMT_TYPE :
                                  PIO_CPL_FMT_TYPE),        // 7,req_compl_wd_q 信号判定这个完成包是否带有数据
                                  {1'b0},                   // 1
                                  req_tc,                   // 3
                                  {4'b0},                   // 4
                                  req_td,                   // 1
                                  req_ep,                   // 1
                                  req_attr,                 // 2
                                  {2'b0},                   // 2
                                  req_len                   // 10
                                  };
            s_axis_tx_tkeep   <= #TCQ 8'hFF;

            state             <= #TCQ PIO_TX_CPLD_QW1_TEMP;
            end
        else
            state             <= #TCQ PIO_TX_RST_STATE;

           end //PIO_TX_CPLD_QW1_FIRST
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

接着准备发送第二个DW,这个状态比较简单,只设置了valid信号,说明主机准备好发送第二帧。接着跳转到PIO_TX_CPLD_QW1 状态

        PIO_TX_CPLD_QW1_TEMP : begin   
            s_axis_tx_tvalid <= #TCQ 1'b1;
            state             <= #TCQ PIO_TX_CPLD_QW1;
        end
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

接下来 PIO_TX_CPLD_QW1状态,在这个状态中,首先通过s_axis_tx_tready 信号判断从设备是否准备好接受信号;由于完成包由3DW标头和1DW的数据构成,总共2帧,所以这一帧是最后一帧,因此此时设置s_axis_tx_tlast为1表明这是最后一帧;然后设置s_axis_tx_tvalid为1表明此时主设备准备好发送数据;接着就根据完成包格式拼接发送数据。拼接完成后通过req_compl_wd_q设置s_axis_tx_tkeep信号,由于一次传输64bit,所以第二帧刚好1DW标头+1DW数据,这一帧都有效,所以s_axis_tx_tkeep为FF,如果这个完成包不带数据,即req_compl_wd_q无效,则最后一帧数据中只有1DW标头,那么s_axis_tx_tkeep就为0F。
至此这个带数据的完成包就发送完成了,所以设置compl_done有效,这个信号返回到接收引擎中,使得接收引擎准备接收下一个TLP,设置compl_busy_i无效,说明又可以发送完成包了,同时状态机跳转至PIO_TX_RST_STATE状态。

        PIO_TX_CPLD_QW1 : begin

          if (s_axis_tx_tready)
          begin

            s_axis_tx_tlast  <= #TCQ 1'b1;
            s_axis_tx_tvalid <= #TCQ 1'b1;
            // Swap DWORDS for AXI
            s_axis_tx_tdata  <= #TCQ {        // Bits
                                  rd_data,    // 32,数据
                                  req_rid,    // 16,requester id
                                  req_tag,    //  8,
                                  {1'b0},     //  1
                                  lower_addr  //  7
                                  };

            // Here we select if the packet has data or
            // not.  The strobe signal will mask data
            // when it is not needed.  No reason to change
            // the data bus.
            if (req_compl_wd_q)
              s_axis_tx_tkeep <= #TCQ 8'hFF;
            else
              s_axis_tx_tkeep <= #TCQ 8'h0F;


            compl_done        <= #TCQ 1'b1;
            compl_busy_i      <= #TCQ 1'b0;
            state             <= #TCQ PIO_TX_RST_STATE;

          end // if (s_axis_tx_tready)
          else
            state             <= #TCQ PIO_TX_CPLD_QW1;

        end // PIO_TX_CPLD_QW1

        default : begin
          // case default stmt
          state             <= #TCQ PIO_TX_RST_STATE;
        end

      endcase
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

MEM_ACCESS

同样我们先看输入输出信号

	module PIO_EP_MEM_ACCESS  #(
	parameter TCQ = 1
	) (
	
		clk,
		rst_n,
		
		 // Read Access

		rd_addr,     // I [10:0]  Read Address
		rd_be,       // I [3:0]   Read Byte Enable
		rd_data,     // O [31:0]  Read Data

		// Write Access

		wr_addr,     // I [10:0]  Write Address
		wr_be,       // I [7:0]   Write Byte Enable
		wr_data,     // I [31:0]  Write Data
		wr_en,       // I         Write Enable
		wr_busy      // O         Write Controller Busy

	);

	input            clk;
	input            rst_n;

	//  Read Port

	input  [10:0]    rd_addr;
	input  [3:0]     rd_be;
	output [31:0]    rd_data;

	//  Write Port

	input  [10:0]    wr_addr;
	input  [7:0]     wr_be;                               //写字节使能
	input  [31:0]    wr_data;
	input            wr_en;                               
	output           wr_busy;
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

FPGA存储器的状态

	localparam PIO_MEM_ACCESS_WR_RST   = 3'b000;          //复位状态
	localparam PIO_MEM_ACCESS_WR_WAIT  = 3'b001;          //写等待状态
	localparam PIO_MEM_ACCESS_WR_READ  = 3'b010;          //读出原数据状态
	localparam PIO_MEM_ACCESS_WR_WRITE = 3'b100;          //写状态
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

一些中间变量,读操作就是将指定地址下的数据读取出来;存储器写操作就是不太一样,是先将该地址的数据读取出来,然后将数据改变成需要写入的数据,最后将数据写入。因此写操作多了很多中间变量。

	wire   [31:0]     rd_data;                                                //读数据

	reg   [31:0]      rd_data_raw_o;                                          //读数据的列

	wire  [31:0]     rd_data0_o, rd_data1_o, rd_data2_o, rd_data3_o;          //读数据0,1,2,3

	wire             rd_data0_en, rd_data1_en, rd_data2_en, rd_data3_en;      //读数据0使能,1使能,2使能,3使能

	wire             wr_busy;             //写繁忙控制
	reg              write_en;            //写使能
	reg   [31:0]     post_wr_data;        //要写入bram的数据
	reg   [31:0]     w_pre_wr_data;       //之前这个地址上的数据

	reg   [2:0]      wr_mem_state;        //写存储器状态

	reg   [31:0]     pre_wr_data;         //之前的数据,pre_wr_data 中的值就是存储器中读出的值
	wire  [31:0]     w_pre_wr_data0;      //写预存的数据0,
	wire  [31:0]     w_pre_wr_data1;
	wire  [31:0]     w_pre_wr_data2;
	wire  [31:0]     w_pre_wr_data3;

	wire  [7:0]      w_pre_wr_data_b0;    //写预存的字节0 从pre_wr_data中读取赋值
	wire  [7:0]      w_pre_wr_data_b1;
	wire  [7:0]      w_pre_wr_data_b2;
	wire  [7:0]      w_pre_wr_data_b3;

	wire  [7:0]      w_wr_data_b0;        //写字节0,从wr_data中读取赋值
	wire  [7:0]      w_wr_data_b1;
	wire  [7:0]      w_wr_data_b2;
	wire  [7:0]      w_wr_data_b3;
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

接下来我们看一下状态转移,首先是复位状态,在该状态下,判断wr_en信号,这是从接收引擎中引入的信号,表示写存储器写控制状态机开始,然后进去写等待状态PIO_MEM_ACCESS_WR_WAIT。

	 case ( wr_mem_state )

    PIO_MEM_ACCESS_WR_RST : begin

      if (wr_en)
      begin // read state
        wr_mem_state <= #TCQ PIO_MEM_ACCESS_WR_WAIT; //Pipelining happens in RAM's internal output reg.
      end
      else
      begin
        write_en <= #TCQ 1'b0;
        wr_mem_state <= #TCQ PIO_MEM_ACCESS_WR_RST;
      end
    end // PIO_MEM_ACCESS_WR_RST
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

若存储器状态为写等待状态,在这个状态下,程序先让write_en无效,表示这时不能对存储器进行写入,而准备将这个地址下的数据读出,进入下一状态。

    PIO_MEM_ACCESS_WR_WAIT : begin

      write_en <= #TCQ 1'b0;
      wr_mem_state <= #TCQ PIO_MEM_ACCESS_WR_READ ;

    end // PIO_MEM_ACCESS_WR_WAIT
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

若存储器状态为读出原数据状态, 在这个状态下,将w_pre_wr_data的值赋给pre_wr_data,而w_pre_wr_data的值就是从存储器中读出的值,也就是说,pre_wr_data 中的值也是存储器中读出的值,随后进入写状态。

    PIO_MEM_ACCESS_WR_READ : begin

        // Now save the selected BRAM B port data out现在保存选定的BRAM B端口数据,BRAM端口B,可以对BYTE进行操作

        pre_wr_data <= #TCQ w_pre_wr_data;
        write_en <= #TCQ 1'b0;
        wr_mem_state <= #TCQ PIO_MEM_ACCESS_WR_WRITE;

    end // PIO_MEM_ACCESS_WR_READ
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

若存储器状态为写状态,post数据(即为合并后的新使能的数据)写入目标bram内
///根据wr_be字节使能信号选择不同的新数据中的字节或先前的数据的字节,将这些选择的字节按顺序拼接成一个完整的数据post_wr_data
由于这个数据是接收引擎中的携带的数据,而且是最后一帧的最后一个DW,所以需要通过LAST DW BE信号判定这个DW是否都有效,而wr_be信号就是LAST DW BE信号,由程序可知,如果这个DW中的某一个字节有效,则就将这个字节写入存储器,否则这个字节的数据还是原来的数据。这就是存储器的写操作原则。

    PIO_MEM_ACCESS_WR_WRITE : begin

      //Merge new enabled data and write target BlockRAM location合并新使能的数据并写入目标 BlockRAM 位置
        若写字节3使能则选择 w_wr_data_b3(新的数据字节)若未使能则选择w_pre_wr_data_b3(当前的数据字节)进行拼接
      post_wr_data <= #TCQ {{wr_be[3] ? w_wr_data_b3 : w_pre_wr_data_b3},
                           {wr_be[2] ? w_wr_data_b2 : w_pre_wr_data_b2},
                           {wr_be[1] ? w_wr_data_b1 : w_pre_wr_data_b1},
                           {wr_be[0] ? w_wr_data_b0 : w_pre_wr_data_b0}};
      write_en     <= #TCQ 1'b1;
      wr_mem_state <= #TCQ PIO_MEM_ACCESS_WR_RST;

    end // PIO_MEM_ACCESS_WR_WRITE

    default : begin
      // default case stmt
      wr_mem_state <= #TCQ PIO_MEM_ACCESS_WR_RST;
    end // default

  endcase // case (wr_mem_state)	
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

读状态就是按地址进行读取即可。

	//  Memory Read Controller若读地址高2位为00,读取data0

	assign rd_data0_en = {rd_addr[10:9]  == 2'b00};
	assign rd_data1_en = {rd_addr[10:9]  == 2'b01};
	assign rd_data2_en = {rd_addr[10:9]  == 2'b10};
	assign rd_data3_en = {rd_addr[10:9]  == 2'b11};
	///只要读地址或读数据变化时,读地址高2位为00,读数据列为data0
	always @(rd_addr or rd_data0_o or rd_data1_o or rd_data2_o or rd_data3_o)
	begin

		case ({rd_addr[10:9]}) // synthesis parallel_case full_case

  		2'b00 : rd_data_raw_o = rd_data0_o;
 		2'b01 : rd_data_raw_o = rd_data1_o;
  		2'b10 : rd_data_raw_o = rd_data2_o;
  		2'b11 : rd_data_raw_o = rd_data3_o;

		endcase

	end

	// Handle Read byte enables若读字节0使能,则取raw【7:0】,否则为全0 

	assign rd_data = {{rd_be[0] ? rd_data_raw_o[07:00] : 8'h0},
                  {rd_be[1] ? rd_data_raw_o[15:08] : 8'h0},
                  {rd_be[2] ? rd_data_raw_o[23:16] : 8'h0},
                  {rd_be[3] ? rd_data_raw_o[31:24] : 8'h0}};
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">
data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/qq_44898332/article/details/145137480","extend1":"pc","ab":"new"}">>
注:本文转载自blog.csdn.net的qq_44898332的文章"https://blog.csdn.net/qq_44898332/article/details/145137480"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接

评论记录:

未查询到任何数据!