module demo_packet_generator(
  Reset_n,
  Clk,

  //--- Wishbone interface
  STB_I,
  CYC_I,
  ADR_I,
  WE_I,
  DAT_I,
  DAT_O,
  ACK_O,

  //--- Packet Descriptor Memory interface
  // RdData_i is always valid exactly one clock after Addr_o changes
  // and Rd_o is asserted
  Rd_o,
  Addr_o,
  RdData_i,

  //--- User (packet) interface
  Rx_mac_ra,
  Rx_mac_rd,
  Rx_mac_data,
  Rx_mac_BE,
  Rx_mac_pa,
  Rx_mac_sop,
  Rx_mac_err,
  Rx_mac_eop,

  Tx_mac_wa,
  Tx_mac_wr,
  Tx_mac_data,
  Tx_mac_BE,
  Tx_mac_sop,
  Tx_mac_eop
);

  input         Reset_n;
  input         Clk;

  //--- Wishbone interface
  input         STB_I;
  input         CYC_I;
  input [1:0]   ADR_I;
  input         WE_I;
  input [15:0]  DAT_I;
  output [15:0] DAT_O;
  output        ACK_O;

  //--- Packet Generator interface
  // RdData_o is always valid exactly one clock after Addr_o changes
  // and Rd_o is asserted
  output        Rd_o;
  output [13:0] Addr_o;
  input [31:0]  RdData_i;

  //--- User (packet) interface
  input         Rx_mac_ra;
  output        Rx_mac_rd;
  input [31:0]  Rx_mac_data;
  input [1:0]   Rx_mac_BE;
  input         Rx_mac_pa;
  input         Rx_mac_sop;
  input         Rx_mac_err;
  input         Rx_mac_eop;

  input         Tx_mac_wa;
  output        Tx_mac_wr;
  output [31:0] Tx_mac_data;
  output [1:0]  Tx_mac_BE;
  output        Tx_mac_sop;
  output        Tx_mac_eop;

  //-------------------------------------------------------------------------
  // Local declarations
  //-------------------------------------------------------------------------

  reg        ACK_O;
  reg [15:0] DAT_O;

  reg        Rd_o;
  reg        Tx_mac_wr;
  reg [1:0]  Tx_mac_BE;
  reg        Tx_mac_sop;
  reg        Tx_mac_eop;

  //--- Wishbone interface --------------------------------------------------

  reg [1:0] PG_CFG;
  wire      PG_Enable = PG_CFG[0];
  
  always @( negedge Reset_n or posedge Clk )
    if ( ~Reset_n )
      begin
        ACK_O <= 0;
        DAT_O <= 'b0;

        PG_CFG <= 2'h0;
      end
    else
      begin
        ACK_O <= 0;
        if ( CYC_I & STB_I )
          begin
            ACK_O <= ~ACK_O; // Generate single cycle pulse!
            if ( WE_I )
              begin
                PG_CFG <= DAT_I;
              end
            else
              begin
                DAT_O[1:0] <= PG_CFG;
              end
          end
      end

  //--- Packet Generator FSM ------------------------------------------------

  parameter  PG_FSM_STATE_IDLE               = 3'h0;
  parameter  PG_FSM_STATE_LD_DESC_1          = 3'h1;
  parameter  PG_FSM_STATE_LD_DESC_2          = 3'h2;
  parameter  PG_FSM_STATE_RD_HEADER          = 3'h3;
  parameter  PG_FSM_STATE_PAYLOAD_SEQ_NUMBER = 3'h4;
  parameter  PG_FSM_STATE_PAYLOAD            = 3'h5;
  parameter  PG_FSM_STATE_DONE               = 3'h6;
  reg [2:0]  PG_FSM_State;

  reg [9:0] DescHigh; // Selects currente descriptor
  reg [3:0] DescLow; // Index into a single descriptor (16 entries)

  reg        PDM_CFG1_LAST;
  reg [3:0]  PDM_CFG1_REPEAT;
  reg [3:0]  PDM_CFG1_HDRLEN;
  reg [15:0] PDM_CFG2_PAYLDLEN;

  reg [31:0] Tx_mac_data_reg;
  reg        WriteHeader;
  reg [15:0] PayloadRemaining;
  reg [31:0] PacketSequenceNumber;
  reg [31:0] Payload;
  
  always @( negedge Reset_n or posedge Clk )
    if ( ~Reset_n )
      begin
        PG_FSM_State <= PG_FSM_STATE_IDLE;

        Rd_o <= 0;

        DescHigh <= 10'b0;
        DescLow  <= 4'b0;

        Tx_mac_wr  <= 0;
        Tx_mac_sop <= 0;
        Tx_mac_eop <= 0;
        Tx_mac_BE  <= 2'b00;

        Tx_mac_data_reg <= 32'b0;
        WriteHeader <= 0;
        PayloadRemaining <= 16'd0;

        PacketSequenceNumber <= 32'd0;
        Payload <= 32'h0;

        { PDM_CFG1_HDRLEN, PDM_CFG1_REPEAT, PDM_CFG1_LAST, PDM_CFG2_PAYLDLEN } <= 'b0;
      end
    else
      begin
        casez ( PG_FSM_State )
          PG_FSM_STATE_IDLE:
            if ( PG_Enable )
              begin
                PG_FSM_State <= PG_FSM_STATE_LD_DESC_1;
                Rd_o <= 1;
              end
            else
              begin
                DescHigh <= 10'b0;
                DescLow  <= 4'b0;
              end

          PG_FSM_STATE_LD_DESC_1:
            begin
              PG_FSM_State <= PG_FSM_STATE_LD_DESC_2;

              DescLow <= DescLow + 1;
            end

          PG_FSM_STATE_LD_DESC_2:
            begin
              PG_FSM_State <= PG_FSM_STATE_RD_HEADER;

              { PDM_CFG1_LAST, PDM_CFG1_REPEAT, PDM_CFG1_HDRLEN, PDM_CFG2_PAYLDLEN } <=
                { RdData_i[31], RdData_i[23:20], RdData_i[19:16], RdData_i[15:0] };
            end

          PG_FSM_STATE_RD_HEADER:
            begin
              Tx_mac_wr <= 0;
              if ( Tx_mac_wa )
                begin
                  // Space in Tx FIFO - write next header word
                  DescLow <= DescLow + 1;
                  Tx_mac_wr <= 1;
                  Tx_mac_sop <= ( DescLow == 1 ); // Assert SOP on first header word
                  WriteHeader <= 1;
                  if ( DescLow == PDM_CFG1_HDRLEN )
                    begin
                      // The requested number of header words has been read
                      // - proceed to generate packet payload
                      PG_FSM_State <= PG_FSM_STATE_PAYLOAD_SEQ_NUMBER;
                      PayloadRemaining <= PDM_CFG2_PAYLDLEN;
                    end
                end
            end

          PG_FSM_STATE_PAYLOAD_SEQ_NUMBER:
            begin
              WriteHeader <= 0;
              Tx_mac_data_reg <= PacketSequenceNumber;
              Tx_mac_wr <= 0;
              Tx_mac_sop <= 0;
              if ( Tx_mac_wa )
                begin
                  Tx_mac_wr <= 1;
                  PG_FSM_State <= PG_FSM_STATE_PAYLOAD;
                  Payload <= 32'h01020304;
                  PayloadRemaining <= PayloadRemaining - 4;
                end
            end

          PG_FSM_STATE_PAYLOAD:
            begin
              Tx_mac_data_reg <= Payload;
              Tx_mac_wr <= 0;
              if ( Tx_mac_wa )
                begin
                  Tx_mac_wr <= 1;
                  Tx_mac_data_reg <= Payload;
                  Payload[31:24] <= Payload[31:24] + 8'h04;
                  Payload[23:16] <= Payload[23:16] + 8'h04;
                  Payload[15: 8] <= Payload[15: 8] + 8'h04;
                  Payload[ 7: 0] <= Payload[ 7: 0] + 8'h04;
                  PayloadRemaining <= PayloadRemaining - 4;
                  if ( PayloadRemaining <= 4 )
                    begin
                      PG_FSM_State <= PG_FSM_STATE_DONE;

                      Tx_mac_eop <= 1;
                      // Indicate how many bytes are valid in this last transfer
                      Tx_mac_BE <= PayloadRemaining[1:0];
                    end
                end
            end

          PG_FSM_STATE_DONE:
            begin
              // TBD: Add support for REPEAT, NEXT & LAST!
              Tx_mac_wr <= 0;
              Tx_mac_eop <= 0;
            end
        endcase
      end

  //-------------------------------------------------------------------------

  assign Tx_mac_data = WriteHeader ?
                       RdData_i : Tx_mac_data_reg;

  assign Addr_o = { DescHigh, DescLow };

  assign Rx_mac_rd = 0;

endmodule