Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
12 years ago

What's Wrong with This SV Code Involving Interfaces?

I'm trying to set initial conditions for registers in an array of interfaces. Doing it manually seems to work. In my project, this builds in Quartus without errors:

    initial begin
        ports.done = '1;
        ports.done = '1;
        ports.done = '1;
        ports.done = '1;
    end

However, changing that block of code to this causes the build to fail:

    initial begin
        for(int n=0; n<4; n++) begin
            ports.done = '1;
        end
    end

In the second case I get an error of 'can't resolve reference to object "ports"' as though the array doesn't exist. Am I making a dumb mistake or trying something that isn't possible? My eventual goal is to have a parameterized number of ports, but that requires being able to make assignments using loops.

5 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Try using a generate block. I do it all the time. I suspect you cannot do this because interfaces appear as unpacked data structures in the memory.

    -sanjay
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I think I see what you’re saying, but I’m struggling to make it work for what I actually want to do. As far as seeing what you mean, this design builds fine for me in Quartus:

    interface my_interface;
        logic      some_byte;
    endinterface
    module fpga_test(
        input   wire                clk,
        my_interface                ports 
    );
        genvar n;
        generate 
            for(n=0; n<4; n++) begin: init_ports
                initial ports.some_byte = '1;
                
                always_ff @(posedge clk) ports.some_byte <= ports.some_byte + 1'd1;
            end
        endgenerate
    endmodule

    In practice, though, I’m trying to use this in an application where all the ports are sharing a single resource. I need priority so that when multiple ports request the resource, only the lowest index port is given access.

    I came up with an example that’s closer to what I want to do. I started by writing it out explicitly. The idea here is that the fpga_test module initializes and increments the some_byte register in each port. Each port has a an increment_enable signal that comes from the outside world. If increment_enable is asserted on any of the ports, I want only the lowest numbered port to increment. That simulates the concept of priority that I’m trying to achieve. This explicit version builds in Quartus:

    interface my_interface;
        logic      some_byte;
        logic           increment_enable;   //This is an input from another module
    endinterface
    module fpga_test(
        input   wire                clk,
        my_interface                ports 
    );
        initial begin
            ports.some_byte = '1;
            ports.some_byte = '1;
            ports.some_byte = '1;
            ports.some_byte = '1;
        end
        
        always_ff @(posedge clk) begin
            //Check if any of the increment_enable lines are high. If one or more is high, only act based on the
            //lowest index.
            if(increment_now) ports.some_byte <= ports.some_byte + 1'd1;
        end
        
        function increment_now; //Check if any increment_enable lines are high
            logic result = 0;
            //Written like this so that it's obvious how to make it into a for loop
            result = result | ports.increment_enable;
            result = result | ports.increment_enable;
            result = result | ports.increment_enable;
            result = result | ports.increment_enable;
            return result;
        endfunction
        
        function  get_increment_index; //Get the index of the lowest active increment_enable
            logic  result = '0;
            //Written like this so that it's obvious how to make it into a for loop
            if      (ports.increment_enable) result = 2'd0;
            else if (ports.increment_enable) result = 2'd1;
            else if (ports.increment_enable) result = 2'd2;
            else if (ports.increment_enable) result = 2'd3;
            return result;
        endfunction
    endmodule

    Can this be made to work with loops so that I can parameterize the number of ports? I get build errors when trying to make almost any of that loop-based. I can't even get the functions alone to build when written as loops.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Arrays of instances cannot be dynamically selected with a variable index. You must use a constant select (provided by a generate as well). The reason for this is because of parameter overrides that can make each element a unique kind if instance. With a variable array, the elements must be identical.

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Thank you for the clarification. That is annoying, because in this case there are no parameters that could lead to the instances being different. It’s too bad there’s no exception for a case like this where every element of the array is identical.

    Building from your comment, I wondered what would happen if I inserted a struct between the port array and the rest of the code. I used a generate loop to link the port array with the struct array. Then I just updated my loops to reference the struct array instead of the port array.

    interface my_interface;
        logic      some_byte;
        logic           increment_enable;   //This is an input from another module
    endinterface
    typedef struct {
        logic      some_byte;
        logic           increment_enable;
    } my_struct;
    module fpga_test(
        input   wire                clk,
        my_interface                ports 
    );
        //Create a structure used to work around the issue of not being able to access elements of a port array in loops
        my_struct ports_cheating ; 
        
        //Use generated assignments to make the structure array a proxy for the port array.
        genvar n;
        generate
            for(n=0; n<4; n++) begin: port_assignments
                assign ports.some_byte = ports_cheating.some_byte;
                assign ports_cheating.increment_enable = ports.increment_enable;
            end
        endgenerate
        
        initial begin
            for(int n=0; n<4; n++) ports_cheating.some_byte = '1;
        end
        
        always_ff @(posedge clk) begin
            //Check if any of the increment_enable lines are high. If one or more is high, only act based on the lowest index.
            if(increment_now) ports_cheating.some_byte <= ports_cheating.some_byte + 1'd1;
        end
        
        function increment_now; //Check if any increment_enable lines are high
            logic result = 0;
            for(int n=0; n<4; n++) result = result | ports_cheating.increment_enable;
            return result;
        endfunction
        
        function  get_increment_index; //Get the index of the lowest active increment_enable
            logic  result = '0;
            for(int n=0; n<4; n++) begin
                if(ports_cheating.increment_enable) begin
                    result = n;
                    break;
                end
            end
            return result;
        endfunction
    endmodule
    

    That builds! It has a couple issues, though. It doesn't run in the Quartus simulator (it looks like ModelSim says there's an error in the .do file that Quartus is generating). I'll try it in a real testbench next. It also has a worrisome warning:

    "Warning (10855): Verilog HDL warning at fpga_test.sv(27): initial value for variable ports_cheating should be constant"

    '1 is clearly a constant value. What is unclear about that? I don't see any inversions happening in the Technology Map Viewer, so it looks like the initial statements are probably being ignored.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    My bad... all the elements of a struct must share the same type of assignment. My combination of continuous and procedural assignments above is what caused the initial state confusion. Structs aren't necessary for what I was trying to do, anyway. Here's a version that works in ModelSim and builds in Quartus:

    interface my_interface;
        logic      some_byte;
        logic           increment_enable;   //This is an input from another module
    endinterface
    module fpga_test(
        input   wire                clk,
        my_interface                ports 
    );
        //Create arrays used to work around the issue of not being able to access elements of a port array in loops
        logic increment_enables ;
        logic  some_bytes ;
        
        //Use generated assignments to make the local arrays proxies for the port array elements.
        genvar n;
        generate
            for(n=0; n<4; n++) begin: port_assignments
                assign ports.some_byte = some_bytes;
                assign increment_enables = ports.increment_enable;
            end
        endgenerate
        
        initial begin
            for(int n=0; n<4; n++) some_bytes = '1;
        end
        
        function increment_now; //Check if any increment_enable lines are high
            automatic logic result = 0;
            for(int n=0; n<4; n++) result = result | increment_enables;
            return result;
        endfunction
        
        function  get_increment_index; //Get the index of the lowest active increment_enable
            automatic logic  result = '0;
            for(int n=0; n<4; n++) begin
                if(increment_enables) begin
                    result = n;
                    break;
                end
            end
            return result;
        endfunction
        
        always_ff @(posedge clk) begin
            //Check if any of the increment_enable lines are high. If one or more is high, only act based on the lowest index.
            if(increment_now) some_bytes <= some_bytes + 1'd1;
        end
    endmodule

    Now that everything is working with loops, it should scale easily. This accomplishes what I was going for, so I have a workable solution now. Maybe someone else will find the code and issues useful.