Search code examples
verilogsystem-verilogquartus

Using a macro gives errors, but putting macro text in explicitly does work


I am using the open source pulp_platform_common_cells which has been implemented for Xilinx FPGAs, and I want to convert it so it also works in Quartus. In Vivado, the project synthesizes fine, but in Quartus, I get the following errors for the stream_throttle.sv file:

  • Verilog HDL syntax error at stream_throttle.sv(90) near text: "("; expecting "endmodule"
  • Verilog HDL syntax error at stream_throttle.sv(90) near text: ")"; expecting ";"
  • Verilog HDL Declaration error at stream_throttle.sv(90): identifier "credit_d" is already declared in the present scope
  • Verilog HDL syntax error at stream_throttle.sv(90) near text: "'0"; expecting an identifier

This error happens at the FF(credit_q, credit_d, '0, clk_i, rst_ni) part where `FF is a macro.
If I put the contents of the macro there instead, everything synthesizes just fine. What could be the issue with this macro?

The error happens near the end of the code. I marked what does work and what doesn't with comments. For simplicity, I put the macro its complaining about at the top.

`define FF (__q, __d, __reset_value, __clk , __arst_n ) \
  always_ff @(posedge (__clk) or negedge (__arst_n)) begin                           \
    if (!__arst_n) begin                                                             \
      __q <= (__reset_value);                                                        \
    end else begin                                                                   \
      __q <= (__d);                                                                  \
    end                                                                              \
  end

/// Throttles a ready valid handshaked bus. The maximum number of outstanding transfers have to
/// be set as a compile-time parameter, whereas the number of outstanding transfers can be set
/// during runtime. This module assumes either in-order processing of the requests or
/// indistinguishability of the request/responses.

module stream_throttle (clk_i, rst_ni, req_valid_i, req_valid_o, req_ready_i, req_ready_o, rsp_valid_i, rsp_ready_i, credit_i);

    /// The maximum amount of allowable outstanding requests
    parameter MaxNumPending = 1,
    /// The width of the credit counter (*DO NOT OVERWRITE*)
    CntWidth = cf_math_pkg::idx_width(MaxNumPending);
    /// The type of the credit counter (*DO NOT OVERWRITE*)
    typedef logic [cf_math_pkg::idx_width(MaxNumPending)-1:0] credit_t;

    /// Clock
    input  logic clk_i;
    /// Asynchronous reset, active low
    input  logic rst_ni;

    /// Request valid in
    input  logic    req_valid_i;
    /// Request valid out
    output logic    req_valid_o;
    /// Request ready in
    input  logic    req_ready_i;
    /// Request ready out
    output logic    req_ready_o;

    /// Response valid in
    input  logic    rsp_valid_i;
    /// Response ready in
    input  logic    rsp_ready_i;

    /// Amount of credit (number of outstanding transfers)
    input  credit_t credit_i;

    // we use a credit counter to keep track of how many transfers are pending at any point in
    // time. Valid is passed-through if there is credit.
    credit_t credit_d;
    credit_t credit_q;

    // we have credit available
    logic credit_available;

    // implement the counter. If credit is available let the valid pass, else block it. Increment
    // the counter once a request happens, decrement once a response arrives. Assumes in-order
    // responses.
    always_comb begin : proc_credit_counter

        // default: keep state
        credit_d = credit_q;

        // on valid outgoing request: count up
        if (req_ready_o & req_valid_o) begin
            credit_d = credit_d + 'd1;
        end

        // on valid response: count down
        if (rsp_valid_i & rsp_ready_i) begin
            credit_d = credit_d - 'd1;
        end
    end

    // credit is available
    assign credit_available = credit_q <= (credit_i - 'd1);

    // a request id passed on as valid if the input is valid and we have credit.
    assign req_valid_o = req_valid_i & credit_available;

    // a request id passed on as ready if the input is ready and we have credit.
    assign req_ready_o = req_ready_i & credit_available;
    // state
    `FF(credit_q, credit_d, '0, clk_i, rst_ni) // THIS DOES NOT WORK
    // always_ff @(posedge (clk_i) or negedge (rst_ni)) begin // THIS DOES WORK                          
    //     if (!rst_ni) begin                                                             
    //     credit_q <= ('0);                                                        
    //     end else begin                                                                   
    //     credit_q <= (credit_d);                                                                  
    //     end                                                                              
    // end

endmodule : stream_throttle

Solution

  • The probelm is the whitespace between FF and ( in the macro declaration. Change:

    `define FF (__q, __d, __reset_value, __clk , __arst_n ) \
    

    to:

    `define FF(__q, __d, __reset_value, __clk , __arst_n ) \
    

    Refer to IEEE Std 1800-2017, section 22.5.1 `define:

    The left parenthesis shall follow the text macro name immediately, with no space in between.

    Since Vivado accepted your syntax, Vivado does not comply with the Std in this case.