Search code examples
crashverilogicarus

Icarus Verilog crash while compiling dynamic memory module


This is my first post on StackOverflow.

I'm a Verilog newbie, though I have significant experience with Python, C, and C++. I am using Icarus Verilog version 10.1.1 on Windows 10, and am trying to write a dynamic memory allocator. For some reason, when this command is run:

iverilog dynmem.v dynmem_test.v -o dynmem_test.out

the following is outputted:

Assertion failed!

Program: c:\iverilog\lib\ivl\ivl.exe
File: ../verilog-10.1.1/pform.cc, Line 333

Expression: lexical_scope

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

What is the problem with my code, and should I submit a bug report for this?

dynmem.v:

`define WORD_SIZE 32

`ifndef DYNMEM_SIZE // size of dynamic memory in words
  `define DYNMEM_SIZE 16384*8/WORD_SIZE // 16 KB
`endif

`define __DYNMEM_BIT_SIZE WORD_SIZE*DYNMEM_SIZE-1 // size of dynamic memory in bits

reg [__DYNMEM_BIT_SIZE:0] dynmem; // dynamic memory
reg [DYNMEM_SIZE:0] allocated; // bitvector telling which words are allocated
reg mutex = 0;

module dynreg(address,
              ioreg,
              read_en,
              write_en,
              realloc_en);
    output reg [WORD_SIZE-1:0] address;
    output reg [WORD_SIZE-1:0] ioreg;
    output reg read_en; // rising edge: put word stored at location address into ioreg
    output reg write_en; // rising edge: put word stored in ioreg into location address
    output reg realloc_en; // rising edge: if ioreg=0, free memory, otherwise reallocate memory into buffer of size ioreg.

    task malloc; // allocate dynamic memory
        output reg [WORD_SIZE-1:0] size;
        output reg [WORD_SIZE-1:0] start;

        unsigned integer size_int = size; // convert size to integer

        reg flag1 = 1;

        while (mutex) begin end // wait on mutex
        mutex = 1; // acquire mutex

        // loop through possible starting locations
        for (index=size_int-1; (index < DYNMEM_SIZE) && flag1; index=index+1) begin
            // check if unallocated
            reg flag2 = 1;
            for (offset=0; (offset < size_int) && flag2; offset=offset+1)
                 if (allocated[index-offset])
                     flag2 = 0;
            if (flag2) begin // if memory block is free
                start = index;
                flag1 = 0; // exit loop
            end
        end

        // mark as allocated
        for (i=0; i<size; i=i+1)
            allocated[start-offset] = 1;

        mutex = 0; // release mutex
    endtask

    task freealloc;
        output reg [WORD_SIZE-1:0] size;
        output reg [WORD_SIZE-1:0] start;

        while (mutex) begin end // wait on mutex
        mutex = 1; // acquire mutex

        // deallocate locations
        for (index=start; index > 0; index=index-1)
            allocated[index] = 0;

        mutex = 0; // release mutex
    endtask

    // internal registers
    unsigned integer start; // start address
    unsigned integer size; // block size
    unsigned integer address_int; // address register converted to int

    initial begin
        // allocate memory
        size = ioreg;
        malloc(size, start);
    end

    always @(posedge(read_en)) begin
        // read memory into ioreg
        address_int = address;
        ioreg[WORD_SIZE-1:0] = dynmem[8*(start+address_int)-1 -:WORD_SIZE-1];
    end

    always @(posedge(write_en)) begin
        // write memory from ioreg
        address_int = address;
        dynmem[8*(start+address_int)-1 -:WORD_SIZE-1] = ioreg[WORD_SIZE-1:0];
    end

    always @(posedge(realloc_en)) begin
        unsigned integer ioreg_int = ioreg; // convert ioreg to integer
        reg [WORD_SIZE-1:0] new_start; // new start address

        if (ioreg_int != 0) begin // if memory is to be reallocated, not freed
            malloc(ioreg, new_start); // allocated new memory
            // copy memory
            for (i=0; i<size; i=i+1)
                dynmem[8*(new_start+i)-1 -:WORD_SIZE-1] = dynmem[8*(start+i)-1 -:WORD_SIZE-1];
        end

        freealloc(size, start); // free previous memory
        // update registers
        size = ioreg_int;
        start = new_start;
    end
endmodule

dynmem_test.v:

module testmodule();
    $monitor ("%g ioreg1=%b ioreg2=%b",
      $time, ioreg1, ioreg2);
    reg [WORD_SIZE-1:0] address1, address2;
    reg [WORD_SIZE-1:0] ioreg1=5, ioreg2=10;
    reg read_en1, read_en2;
    reg write_en1, write_en2;
    reg realloc_en1, realloc_en2;
 #1 dynreg dr1(address1, ioreg1, read_en1, write_en1, realloc_en1);
 #1 dynreg dr2(address2, ioreg2, read_en2, write_en2, realloc_en2);
    address1 = 0;
    ioreg1 = 23;
 #1 write_en1 = 1;
    write_en1 = 0;
    address1 = 2;
    ioreg1 = 53;
 #1 write_en1 = 1;
    write_en1 = 0;
    address1 = 0;
 #1 read_en1 = 1;
    read_en1 = 0;
    address1 = 2;
 #1 read_en1 = 1;
    read_en1 = 0;
 #1 $finish;    
endmodule

UPDATE: C:\iverilog\lib\verilog-10.1.1 doesn't exist, and, in fact, I searched in C:\iverilog for pform.cc and found no results. Strange.


Solution

  • #1 dynreg dr1(address1, ioreg1, read_en1, write_en1, realloc_en1);
    

    Using a delay (#1) on an instance declaration is probably confusing Icarus as much as it's confusing me. (What exactly is supposed to get delayed? Does the instance not exist for one simulation step?)

    Remove those delays, and put all of the code in your testbench following those two instance declarations into an initial block.

    For what it's worth, dynreg is probably not synthesizable as written. It has no clock input, and it contains several loops which cannot be unrolled in hardware.

    UPDATE: C:\iverilog\lib\verilog-10.1.1 doesn't exist, and, in fact, I searched in C:\iverilog for pform.cc and found no results. Strange.

    This path is probably referring to the location of the code on the developer's computer where your copy of Icarus was compiled. Unless you plan on trying to fix the bug that caused this crash yourself, you can safely ignore this.