-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
270 additions
and
169 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
# Basic logic gates | ||
|
||
This weird machine computes several basic logic gates. Specifically, it computes: | ||
|
||
| Operation | #Inputs | #Outputs | | ||
| --------- | ------- | -------- | | ||
| AND | 2 | 1 | | ||
| OR | 2 | 1 | | ||
| NOT | 1 | 1 | | ||
| NAND | 2 | 1 | | ||
| XOR | 2 | 1 | | ||
| MUX | 3 | 1 | | ||
| XOR | 3 | 1 | | ||
| XOR | 4 | 1 | | ||
|
||
We first explain [how to compile and run](#compile-the-weird-machine) this weird machine, and then move on to [the construction](#construction-of-this-weird-machine) of the weird machine. | ||
|
||
## Compile the weird machine | ||
|
||
The following steps are similar to those in "[Compile a weird machine](../../README.md#compile-a-weird-machine)" from the [readme](../../README.md) of Flexo. | ||
Here, we will compile the source code located at [`circuits/gates/test.cpp`](./circuits/gates/test.cpp) and create a simple weird machine. | ||
|
||
### 1. C/C++ to LLVM IR | ||
|
||
The first step to build this weird machine is to compile the source code into LLVM IR. | ||
A [make file](./circuits/gates/Makefile) is prepared to serve this purpose. | ||
Simply run the `make` command under [`circuits/gates/`](./circuits/gates/) to perform this step. | ||
Note that the make file will use the `clang-17` command to compile the source code. | ||
If `clang-17` is not installed, please use [the Docker/Podman container](#install-the-flexo-compiler) used to install Flexo to perform this step: | ||
|
||
```sh | ||
docker run -i -t --rm \ | ||
--mount type=bind,source="$(pwd)"/,target=/flexo \ | ||
flexo \ | ||
make -C /flexo/circuits/gates/ | ||
``` | ||
|
||
This will generate a LLVM IR file, `test.ll`, under [`circuits/gates/`](./circuits/gates/). | ||
|
||
### 2. Use the Flexo compiler to generate a weird machine | ||
|
||
Next, we invoke the Flexo compiler to generate the weird machine. | ||
We use [the Docker/Podman container](#install-the-flexo-compiler) to perform this step: | ||
|
||
```sh | ||
docker run -i -t --rm \ | ||
--mount type=bind,source="$(pwd)"/,target=/flexo \ | ||
flexo \ | ||
bash -c "cd /flexo && ./compile.sh circuits/gates/test.ll circuits/gates/test-wm.ll" | ||
``` | ||
|
||
This command will output the number of wires and gates of each circuit. | ||
Since this weird machine only contains basic logic gates, the number of gates is always one. | ||
The following is the first few lines of the expected output of this command: | ||
|
||
``` | ||
Found weird function: _Z12__weird__andbbRb | ||
[Circuit] Wires: 5 | ||
[Circuit] Gates: 1 | ||
Found weird function: _Z11__weird__orbbRb | ||
[Circuit] Wires: 5 | ||
[Circuit] Gates: 1 | ||
Found weird function: _Z12__weird__notbRb | ||
[Circuit] Wires: 4 | ||
[Circuit] Gates: 1 | ||
... // more gates below (omitted) | ||
``` | ||
|
||
If the compilation process is successful, it will generate another LLVM IR file, `test-wm.ll`, under [`circuits/gates/`](./circuits/gates/) that contains the weird machine. | ||
|
||
|
||
### 3. Generate an executable file | ||
|
||
Finally, we use the LLVM IR file generated by the Flexo compiler to create an executable file. | ||
|
||
If the `clang-17` command is available: | ||
|
||
```sh | ||
clang-17 ./circuits/gates/test-wm.ll -o ./circuits/gates/test.elf -lm -lstdc++ | ||
``` | ||
|
||
Otherwise, use [the Docker/Podman container](#install-the-flexo-compiler) to perform this step: | ||
|
||
```sh | ||
docker run -i -t --rm \ | ||
--mount type=bind,source="$(pwd)"/,target=/flexo \ | ||
flexo \ | ||
clang-17 /flexo/circuits/gates/test-wm.ll -o /flexo/circuits/gates/test.elf -lm -lstdc++ | ||
``` | ||
|
||
After that, `test.elf` will be generated under [`circuits/gates/`](./circuits/gates/). | ||
|
||
### 4. Run the weird machine | ||
|
||
Simply execute the ELF file to run the weird machine. | ||
|
||
```sh | ||
$ ./circuits/gates/test.elf | ||
=== AND gate === | ||
Accuracy: 94.80200%, Error detected: 5.04200%, Undetected error: 0.15600% | ||
Time usage: 0.764 (us) | ||
over 100000 iterations. | ||
=== OR gate === | ||
Accuracy: 89.81200%, Error detected: 9.91900%, Undetected error: 0.26900% | ||
Time usage: 0.785 (us) | ||
over 100000 iterations. | ||
=== NOT gate === | ||
Accuracy: 91.18300%, Error detected: 8.52500%, Undetected error: 0.29200% | ||
Time usage: 0.651 (us) | ||
over 100000 iterations. | ||
... // more gates below (omitted) | ||
``` | ||
|
||
If the accuracy is very low (less than 70%), then the compiler options provided to the Flexo compiler (step 2) may need to be adjusted, or the processor is not supported. | ||
For more information about adjusting the compiler options for the Flexo compiler, please refer to "[Run Flexo on an unsupported processor](#run-flexo-on-an-unsupported-processor)". | ||
|
||
## Construction of this weird machine | ||
|
||
The [source code](./test.cpp) of this weird machine has two main components: | ||
1. The weird machine circuits (the functions with the `__weird__` prefix) | ||
2. The measurement code that computes the accuracy and runtime (the `test_acc` function) | ||
|
||
Only the weird machine circuits are converted into Flexo weird machines. | ||
This means only the weird machine circuits are executed using microarchitectural side effects, while other functions (including the measurement code) are executed normally using architectural components like a normal C++ program. | ||
|
||
### The weird machine circuits | ||
|
||
As an example, the `__weird__and` function implements an AND gate with two inputs and one output: | ||
|
||
```c | ||
bool __weird__and(bool in1, bool in2, bool& out) { | ||
out = in1 & in2; | ||
return out; | ||
} | ||
``` | ||
`in1` and `in2` are the input variables, and the output is stored in `out`. | ||
The return type of this function is `bool`, and the return value contains the error detection result. | ||
Even though this function returns `out`, **the return value does not contain the output value**. | ||
Instead, it returns the **error detection** result. | ||
This means this function returns `true` when it detects an error, and it returns `false` when no error is detected. | ||
### The measurement code | ||
The `test_acc` function runs the weird machine circuits and measures the accuracy and runtime. | ||
The accuracy is defined as the number of runs that the output is correct over the total number of runs. | ||
Detected error rate and undetected error rate have similar definition, so `accuracy` + `error detected` + `undetected error` should always be 100%. | ||
The definition of a correct run, a detected error, and an undetected error is as follows: | ||
Suppose a gate should output the binary value `1`. | ||
There are three cases. | ||
1. If we run this gate and it indeed outputs `1`, then this is a correct run. | ||
2. If we run this gate and it outputs `0`, then this is an undetected error. | ||
3. If we run this gate and it neither outputs `0` nor outputs `1`, then this is a detected error. | ||
When the third case happens (the gates outputs neither `0` nor `1`), the return value of the weird machine circuit will be `true`, which indicates the error detection result, and the output value `out` may contain any value with no useful information. | ||
Here is the measurement code for the AND gate. | ||
```c | ||
test_acc("AND", 2, [](unsigned in) { | ||
bool out; | ||
bool errorDetected = __weird__and(in & 1, (in & 2) >> 1, out); | ||
unsigned result = (errorDetected << 1) | (((in & 3) == 3) != out); | ||
return result; | ||
}); | ||
``` | ||
|
||
The error detection result (the return value of `__weird__and`) is stored in a boolean variable called `errorDetected`, and the output value is stored in `out` (also a `bool`). | ||
It checks whether the output is correct using `((in & 3) == 3) != out`, which is then set as the first bit of `result`. | ||
The second bit of `result` is set to `errorDetected`. | ||
|
||
This explains how the main body of `test_acc` works. | ||
In the definition of the `test_acc` function, there is a `for` loop that keep executes the logic gates, and it contains an `if` statement to check whether the gate outputs correctly or not: | ||
|
||
```c | ||
if (result == 0) { | ||
++tot_correct_counts; | ||
} | ||
else if (result & 2) { | ||
++tot_detected_counts; | ||
} | ||
else { | ||
++tot_error_counts; | ||
} | ||
``` | ||
|
||
If `result` is `0`, then it means there is no error detected (second bit unset), and the output is correct (first bit unset). | ||
When the second bit is set (`result & 2`), then this gate detects an error. | ||
Otherwise, this gate outputs incorrectly. | ||
|
||
After the `for` loop's execution, this function outputs the average runtime of each iteration (one execution of the gate), and the accuracy, detected error rate, and the undetected error rate. |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.