This is an old revision of the document!


File interaction

This library exports functions for basic file interaction. Files in actsim can be accessed using the common simulator prefix and file IDs. The common prefix is set using an actsim configuration file. The following is a snippet from the default actsim configuration.

#
# parameters for external file names for sim::file* functions
#
begin file
    # by default, file names will be prefix.<number>
    string prefix "_infile_"

    # alternatively, you can specify file names here
    # where 0 = first file name, 1 = second file name, etc.
    # string_table name_table "file1.in" "file2.in"

    # by default, output file names will be prefix.<number>
    string outprefix "_outfile_"
    # alternatively, you can specify output file names here
    # where 0 = first file name, 1 = second file name, etc.
    # string_table outname_table "file1.in" "file2.in"
end

This configuration can be found on your local machine by going to $ACT_HOME/conf/generic/actsim.conf. Please see the actsim documentation page for more details.

By default, the prefix for input files is infile. and outfile. for output files, where the period is followed by the file ID. This simulation library allows you simultaneous read access to a given file by multiple processes. Simultaneous read and write access, or multi-write access is not allowed. The following functions are provided inside the sim::file namespace:

// read from files
export function openr (int<32> file_id) : int<32>;
export function read (int<32> reader_id) : int<64>;
export function eof (int<32> reader_id) : bool;
export function closer (int<32> reader_id) : bool;
 
// write to files
export function openw (int<32> file_id) : int<32>;
export function write (int<32> writer_id; int<64> val) : bool;
export function closew (int<32> writer_id) : bool;

File reader example

We will go over a simple example for reading an input file. Input files can provide integers of up to 32 bid width. Numbers are interpreted as base 10 by default, base 16, 8, and 2 are also allowed by the use of prefixes. Input files also support comments using # as a prefix. Files can contain a trailing newline character after the last value, multiple newlines or comments after the last input value are currently not supported (to be changed in the future).

In this example, we will construct a simple file source, which reads from infile.0 using the simulation library (however, this library also already provides a fully functional one, see the page on sources, which we recommend using instead).

defproc source_file (chan!(int<32>) O)
{
    int<32> buf;
    int reader_id;
    bool success;
 
    chp {
        // we will put all further code here
    }
}

File interactions are not synthesizable. All of our code is CHP based. You might have spotted the variables at the beginning of the block, we will explain them shortly.

First, we need to open the file for reading. This will return a reader ID, which is quite similar to a file descriptor on POSIX systems. As indicated by the comment, this will happen in the CHP block.

// open the file and get a reader ID
reader_id := sim::file::openr (0);
 
// this is not necessary but good practice error checking
assert (reader_id != 0, "Oh no! We failed to open input file with ID 0!");

openr will return a number larger than 0, if the file was successfully opened; a reader ID of 0 will indicate that something went wrong. It is good practice to make sure that everything worked; a call to assert is an easy solution for this.

We have now opened file 0 for reading and saved the reader ID in reader_id. The next step is to read a value:

// read from the file
buf := sim::file::read(reader_id);

Since we are writing a source, we then go on to send this value to our output channel:

// send the value off
O!buf;

We want to repeat this process until we reach the end of the input file. For this, we simply put these two steps into a do-while loop:

// read until the file reports EOF and send it to the output channel
*[
    // read, then send the value
    buf := sim::file::read(reader_id);
    O!buf;
<- ~sim::file::eof (reader_id) ];

In the end, we want to close the file again. Similarly to openr, closer's return value will allow us to make sure everything worked fine.

// close the file
success := sim::file::closer (reader_id);
 
// making sure everything worked
assert (success, "Oh no! We failed to close input file with ID 0!")

Putting things together, the full example looks like this:

defproc source_file (chan!(int<32>) O)
{
    int<32> buf;
    int reader_id;
    bool success;
 
    chp {
        // open the file and get a reader ID
        reader_id := sim::file::openr (0);
 
        // this is not necessary but good practice error checking
        assert (reader_id != 0, "Oh no! We failed to open input file with ID 0!");
 
        // read until the file reports EOF and send it to the output channel
        *[
            // read, then send the value
            buf := sim::file::read(reader_id);
            O!buf;
        <- ~sim::file::eof (reader_id) ];
 
        // close the file
        success := sim::file::closer (reader_id);
 
        // making sure everything worked
        assert (success, "Oh no! We failed to close input file with ID 0!")
    }
}

File writer example

We will go over a simple example for writing an output file. Without writing C code, which hooks into the already provided API, the file writer can only push simple integer values to an output file.

In this example, we will construct a simple file sink, which writes to outfile.0 using the simulation library (however, this library also already provides a fully functional one, see the page on sinks, which we recommend using instead).

defproc sink_file (chan!(int<32>) I)
{
    int<32> buf;
    int writer_id;
    bool success;
 
    chp {
        // we will put all further code here
    }
}

File interactions are not synthesizable. All of our code is CHP based. You might have spotted the variables at the beginning of the block, we will explain them shortly.

First, we need to open the file for writing. This will return a writer ID, which is quite similar to a file descriptor on POSIX systems. As indicated by the comment, this will happen in the CHP block.

// open the file and get a writer ID
writer_id := sim::file::openw (0);
 
// this is not necessary but good practice error checking
assert (writer_id != 0, "Oh no! We failed to open output file with ID 0!");

openw will return a number larger than 0, if the file was successfully opened; a writer ID of 0 will indicate that something went wrong. It is good practice to make sure that everything worked; a call to assert is an easy solution for this.

We have now opened file 0 for writing and saved the writer ID in writer_id. The next step is to receive a value from the input channel and write it to the file:

// receive the value and write to file
I?buf;
success := sim::file::write (write_id, buf)

We want to repeat this process indefinitely (or until the simulation is ended). For this, we simply put these two steps into an infinite loop:

*[
    // receive the value and write to file
    I?buf;
    success := sim::file::write (write_id, buf)
];

Since we don't know how much data will be there, we'll assume the file will only be closed when the simulation ends. In this case, we don't need to explicitly close the file.

Putting things together, the full example looks like this:

defproc sink_file (chan!(int<32>) I)
{
    int<32> buf;
    int writer_id;
    bool success;
 
    chp {
        // open the file and get a writer ID
        writer_id := sim::file::openw (0);
 
        // this is not necessary but good practice error checking
        assert (writer_id != 0, "Oh no! We failed to open output file with ID 0!");
 
        *[
            // receive the value and write to file
            I?buf;
            success := sim::file::write (write_id, buf)
        ];
    }
}