Using pure structures

A pure structure is a user-defined data type that contains only int/bool or other pure structures. Pure structures are analogous to C/C++ structs, and can be used to organize data and make CHP more human-readable. An example of a pure structure is:

deftype cache_entry (bool valid; int<32> data; int<24> tag) { }

An example CHP program that uses this as the data type is:

defproc cache(chan?(int<32>) A; chan?(bool) RD; chan!(int<32>) DI; chan!(int<32>) DO)
{
   cache_entry C[256];
   int<32> addr;
   bool cmd;
   cache_entry ce;
 
   chp {
     *[ A?addr,RD?cmd;
        [cmd -> // read command
              ce := C[addr{7..0}];
             [ ce.valid & ce.tag = addr{31..8} -> log ("cache hit!"); DO!ce.data
             [] else -> log ("cache miss!"); DO!0
             ]
        []~cmd -> // write command
             ce.valid+;
             DI?ce.data;
             ce.tag := addr{31..8};
             C[addr{7..0}] := ce
      ]
   ]
  }                  
}

(Note: this is not meant to be a working cache!)

Methods

We can abstract away the matching function as follows:

deftype cache_entry (bool valid; int<32> data; int<24> tag) 
{ 
    methods {
        function match (int<32> addr) : bool
        {
           chp {
              self := valid & tag = addr{31..8}
           }
         }
    }
}
 
 
defproc cache(chan?(int<32>) A; chan?(bool) RD; chan?(int<32>) DI; chan!(int<32>) DO)
{
   cache_entry C[256];
   int<32> addr;
   bool cmd;
   cache_entry ce;
 
   chp {
     *[ A?addr,RD?cmd;
        [cmd -> // read command
              ce := C[addr{7..0}];
             [ ce.match(addr) -> log ("cache hit!"); DO!ce.data
             [] else -> log ("cache miss!"); DO!0
             ]
       []~cmd -> // write command
             ce.valid+;
             DI?ce.data;
             ce.tag := addr{31..8};
             C[addr{7..0}] := ce
      ]
   ]
  }                  
}

Note that method functions, like other functions in ACT, are pure functions. In other words, they cannot change the fields of the structure. If you need to modify the fields of the structure, special macro methods are supported. These can contain arbitrary CHP fragments.

deftype cache_entry (bool valid; int<32> data; int<24> tag) 
{ 
    methods {
        function match (int<32> addr) : bool
        {
           chp {
              self := valid & tag = addr{31..8}
           }
         }
         macro setval (int<32> addr; int<32> dv) {
            valid+; 
            data := dv;
            tag := addr{31..8}
         }
    }
}
 
defproc cache(chan?(int<32>) A; chan?(bool) RD; chan?(int<32>) DI; chan!(int<32>) DO)
{
   cache_entry C[256];
   int<32> addr, dv;
   bool cmd;
   cache_entry ce;
 
   chp {
     *[ A?addr,RD?cmd;
        [cmd -> // read command
              ce := C[addr{7..0}];
             [ ce.match(addr) -> log ("cache hit!"); DO!ce.data
             [] else -> log ("cache miss!"); DO!0
             ]
       []~cmd -> // write command
             DI?dv;
             ce.setval (addr, dv);
             C[addr{7..0}] := ce
      ]
   ]
  }                  
}

Converting to raw integers

A common requirement is to convert a pure structure data type into a collection of bits, corresponding to an integer with a bit-width that is the sum of the widths of all the elements within the pure structure.

In the example above, this can be done as follows:

int<57> x;
...
chp {
   ...;
   x := int(ce);
   ...
 
   ce := cache_type(x);
   ...
}

The order in which the bits are packed is from left to right in the structure definition, with the left-most field corresponding to the most significant bits of the integer.