> ## Documentation Index
> Fetch the complete documentation index at: https://sdk.cerebras.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Microthread IDs

> Use explicit microthread IDs on WSE-3 to control which hardware microthread executes a given asynchronous DSD operation.

On WSE-3 it is possible to explicitly specify the microthread ID of an
asynchronous DSD operation (i.e., a DSD operation with the `.async`
setting set to `true` as explained in [Asynchronous DSD Operations](/csl/language/dsds#asynchronous-dsd-operations)).

## Constructor and Type

Microthread ID expressions have type `ut_id`. Values of `ut_id`
type can be constructed from integers using
`@get_ut_id` (see [@get\_ut\_id](/csl/language/builtins#@get_ut_id)).

CSL exposes **8 microthread IDs** on WSE-3, addressable as
`@get_ut_id(0)` through `@get_ut_id(7)`. Passing an integer outside
this range to `@get_ut_id` is a compile-time error.

## Relationship between microthread IDs and queue IDs

A microthread on WSE-3 is the hardware context that drives an in-flight
asynchronous DSD operation. Each microthread is associated with a fabric
input queue or output queue for the lifetime of the operation, and the
microthread ID and queue ID are independent numbers. By default, an
operation's microthread ID is determined by the queue ID of its
highest-priority fabric operand (destination, then src0, then src1);
the `.ut_id` setting overrides this default.

Using a microthread ID different from the queue ID is useful when two
concurrent operations share a queue but need to be tracked as distinct
microthreads, or when explicit `@block` / `@unblock` of a specific
microthread is desired.

## Usage and Semantics

An asynchronous DSD operation can be assigned a microthread ID through
the `.ut_id` setting of the DSD operation’s configuration struct
or through the `.ut_id` setting of the `@load_to_dsr`’s configuration
struct of explicit DSR operands. The value of the `.ut_id` setting
must be comptime-known.

If multiple DSR operands have the `.ut_id` setting specified then
the hardware will pick one of them according to the following order:

* Destination operand
* First source operand
* Second source operand

The same order will be followed even if a DSD operation consists of
a mix of explicit DSR and DSD operands. In this case the DSD operands
will have the microthread ID specified by the `.ut_id` setting of the
DSD operation’s configuration struct.

If the `.ut_id` setting is not specified in the DSD operation’s
configuration struct or by any of the explicit DSR operands, then
the microthread ID will be the same as the queue ID of the first
fabric operand according to the operand ordering defined above.

## Example

```csl theme={"languages":{"custom":["/languages/csl-tmlanguage.json"]}}
const ut0 = @get_ut_id(0);
const ut1 = @get_ut_id(1);
const ut2 = @get_ut_id(2);
const ut3 = @get_ut_id(3);
const ut4 = @get_ut_id(4);

// These asynchronous DSD operations use two different
// microthreads (i.e., ut0 and ut1) which means that they
// can be executed concurrently even if their queues
// are the same.
@mov16(out_dsd, in_dsd, .{.async = true, .ut_id = ut0});
@mov16(out_dsd, in_dsd, .{.async = true, .ut_id = ut1});

// In the case of explicit DSRs, the microthread ID can be
// specified as a setting to the @load_to_dsr calls.
@load_to_dsr(out_dsr, out_dsd, .{.async = true, .ut_id = ut2});
@load_to_dsr(in_dsr, in_dsd, .{.async = true, .ut_id = ut3});

// This operation will use the microthread ID specified by
// out_dsr according to the .ut_id setting of the respective
// @load_to_dsr call which is ut2. The microthread ID ut4
// will be ignored in this case.
@mov16(out_dsr, in_dsd, .{.async = true, .ut_id = ut4});

// This operation will use the microthread ID specified by
// the operations's .ut_id setting which is ut4. That's because
// out_dsd takes priority over in_dsr.
@mov16(out_dsd, in_dsr, .{.async = true, .ut_id = ut4});

// The .ut_id setting is not required. In this scenario, the
// microthread ID will be the same as the queue ID of the
// highest priority fabric operand (i.e., destination > first source >
// second source ... etc).
@mov16(out_dsd, in_dsd, .{.async = true});
```

## Blocking and Unblocking Microthreads

Microthreads can be blocked/unblocked from a top-level comptime
block or at runtime using the `@block` and `@unblock`
builtins (see [@block](/csl/language/builtins#@block) and
[@unblock](/csl/language/builtins#@unblock)). For example:

```csl theme={"languages":{"custom":["/languages/csl-tmlanguage.json"]}}
 const ut0 = @get_ut_id(0);
 var ut: ut_id;

 task main() void {
   // It is possible to block/unblock a microthread
   // at runtime. The microthread ID may be comptime-known
   // or not.
   @unblock(ut0);
   @block(ut);
 }

comptime {
  // Set microthread 'ut0' as blocked when the program starts.
  @block(ut0);
}
```
