Quirks quickest guide

1 Main Drawing

uvm-mod

2 General

The Quirks architecture don’t look like a real processors architecture. Nevertheless it consists of a set of functional units which can be mapped on units of real processors.

First of all there are no conception of general purpose registers. In other words, there is no fixed set of cells with fixed structure marked out separately. There are no specific operations for particular cells. In the Quirks all cells of computer memory have equal rights.

Let’s see the example. Let memory has the following structure: ‘st1 .= <uint8 arr *>’ (it will be explained a few pages later), i.e. a sequence of bytes. The UVM command

st[1505] <- st[1500];

moves a byte from the cell 1500 to the cell 1505.

The second big difference is an absence of predefined arithmetic and logical operations. Instead of this there is a extendable set of arithmetic modules defined by input/output structure and microprogram (from a VM point of view). Such microprogram is native implemetation of a user defined operation or rule how JIT should generate native code. The set of such modules is grouped into cartridges. Beside of standard cartridge (with base level operation such as add, mul etc.) there can be diferent additional cartridges with more specific operations or operations on special types (for example, matrix arithmetic or string manipulations). There can be processor-oriented cartridges which describe how to implement particular arithmetic action on particular data type with this particular processor by the most effective way.

3 Types of memory

As was mentioned above all memory cells are registers of UVM (to say it more accurately, only that memory which is passed to the UVM by an environment; the role of the environment plays a program which links UVM). ‘st’ - it is what usually named "memory". The environment can pass several continuous blocks of memory to the UVM - st1, st2, ... (each one is specified by a pointer and a size). It is unable for the UVM to use memory out of boundaries of these blocks. st1, st2, ... are named selectors - they are used as handles of that memory1. ‘st’ denotes "static memory".

Besides, the UVM can allocate its own memory blocks. Some amount of memory can be allocated each time the UVM enters a procedure (and deallocated when it leaves the procedure). It is memory for local variables. It is named ‘lv’ (local variables) and can be splitted to pieces by selectors lv1, lv2, ... (we need more than one for some languages which allow blocks with dynamic structures inside procedure, for example Ada). You can access all local variables of the current procedure by those selectors (but only variables of the current procedure!). All lv-selectors names are procedure scoped.

Some amount of memory can be allocated dynamically (as with malloc in C) in a procedure. It is designated as ‘dn’. It can be several dn-selectors as with ‘st’, the only difference is that st-selectors is globally scoped but all dn-selectors use a procedure as a name space.

ct-memory is very similar to ‘st’. It is given by an environment also, it can be represented by several selectors and it is globally scoped also. The difference that for ‘ct’ an environment guarantee immutability all the time the UVM works2. It allows JIT to substitute the values from ‘ct’ as immediate operands. For example, the command

lv[0] <- ct[0];

can be translated by JIT compiler for ‘x86’ as follows:

mov eax,10

(if the containment of ‘ct[0]’ is 10 and ‘lv[0]’ is mapped on eax).

The memory types ‘fp’, ‘dx’, ‘dy’ are named pseudo memory. All the cells of that memory is references to some cells in ‘lv’, ‘st’, ‘ct’, ‘dn’ which are really exists.

An ‘fp’ has only one selector - fp. It is parameters and return values frame of a procedure3. This approach allows don’t use temporary values for some arguments when we build a native code and pass them as a direct reference to a memory or as a register.

dx’ and ‘dy’ is used as a general purpose referenced memory. Each type has its own selector set: dx1, dx2, ... and dy1, dy2, ... . All selector names are procedure scoped. That memory is used, for example, for passing an arguments frame to a procedure.

The ‘au’ memory will be discussed later.

4 A procedure call

Procedure call and return consists of the following steps:

  1. A caller calls the procedure and gives some selector to it as a parameter frame.
  2. In the calling time the UVM stores all local selectors of the caller and the address of return. The passed frame maps onto the new ‘fp’. The ‘lv’ and ‘dn’ selectors are allocated.
  3. The end of the callee code is a return point. The UVM deallocates all local memory and restores selectors of the caller.

5 Descriptors

In contrast to ordinary (hardware) processors any memory cell (‘ct’, ‘st’, ‘dn’, ‘fp’, ‘dx’, ‘dy’, ‘au’, ‘lv’) can have any complexity (from one bit to practically any data structure). Moreover, the structure of a concrete cell can be unknown and passed by an environment. In any case it will be represented by a descriptor. Descriptors can have a tree structure and describe complex types with more simple ones. Atomic types play the role of leafs. Nodes are constructed with complex organizers such as stu (a structure in C), arr (an array) etc.

For example, an array of 40 2-element records can be described as follows:

<<int8 int32 stu> arr 0..39>

A descriptor can organize any type of UVM memory (operations also) and it bounds to a selector at the time the selector appears. (No one can change this binding later). An access to a cell of memory is performed according to the descriptor. Suppose, some selector st1 which is tuned by the above descriptor will be passed by an environment. A code can refer to that memory cells such way:

st1 - the whole array (40 elements)
st1[4] - 5th element of the array (the 2-element structure)
st1[5:0] - the first element of the structure in the 4th array element

A sequence of numbers which is used for cell access (for example, ‘5:0’) is named order. In the example above an order depth can be any from the range 0 .. 2. When one try get access to a cell which is not mentioned in the descriptor the exception is raised.

For example:

st[40]  - exception! (no such cell)
st[0:0:0] - exception! (order too deep)

Thus, if a procedure get a parameter frame (fp) than the procedure can’t get access to anything else beside fp and globally visible selectors (‘st’ and ‘ct’).

6 Arithmetic units

An arithmetic units is a piquant of Quirks.

Each AU is a structure and as any other structure in Quirks it has a descriptor. All kinds of AU structures can be accessed through au-pseudo memory. But structure is not only thing that AU has. Each AU performs some operation. For example, the adder for 32-bit integers can have a structure <int32 int32 int32 stu>, so it can be accessed through three cells. The first and second cells are items, the third is a sum. The adder cells are mapped onto other memory (or AU) cells exactly as any other pseudo memory (‘dx’, ‘dy’) can be mapped. See Mapping in Quirks UVM architecture overview.

The adder function is to sum first two cells and place the result into the third4.

Adders (and another modules, but now the story about adders) can operate with more complex cells, for example with vectors. Here is an example of some simple adder which sums vectors of bytes:

<(int32 int32) <int8 arr *1..*2> <int8 arr *1..*2> <int8 arr *1..*2>
stu>

The elements here are dynamic arrays. The construction ‘(int32 int32)’ defines discriminant cells. The first cell designates the bottom array index (*1), the second designates the top (*2). Thus, it is a adder of variable size vectors (but all three components should have the same size - the descriptor says exactly it).

Let’s imagine a procedure which sums two numbers:


procedure :Sum  is

   declare map
      auSum is fp descriptor;
   map is
      (auSum[0], fp[0]) (auSum[1], fp[1]) (auSum[2], fp[2])
   end map;

begin
      * auSum;
end   :Sum;

auSum is the first adder (as for any other memory can be several selectors, but now we have several ones for the same module also: au1Sum = auSum, au2Sum, au3Sum, ... - all names are procedure scoped).

This procedure is generic (any procedure in Quirks is generic. See The model of a procedure in Quirks UVM architecture overview.) The used adder is designated by a parameter frame descriptor.

7 Summary

In a very compressed form the structure and roles of 4 Quirks UVM modules were examined here. It is a start knowledge which should help to understand our benchmark results and others more detailed documents as well.

The topics such as:

were avoided here. See Quirks UVM architecture in Quirks UVM architecture. See Quirks UVM command set in Quirks UVM command set.


Footnotes

(1)

The first selector for every memory type can be designated without the number (i.e. st1 equal to st etc.).

(2)

It is not exactly reflect the situation. See The model of a procedure in Quirks UVM architecture overview.

(3)

It can be used for access to local variables of a procedure which encloses the current procedure, as in Pascal.

(4)

Really, any adder in Quirks can perform as a minimum 3 operations: cell 3 = cell 1 + cell 2, cell 1 = cell 3 - cell 2 and cell 2 = cell 3 - cell 1. It depends on the cell modes. See Arithmetic modules and a mapping in Quirks UVM architecture overview.