diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 000000000..6ab605ef7 --- /dev/null +++ b/BUILD.md @@ -0,0 +1,101 @@ +# Developing CREATOR + +> [!TIP] +> You can find this information, and more at the [Developer Guide section](https://creatorsim.github.io/creator-wiki/development/setup.html) on the [CREATOR documentation](https://creatorsim.github.io/creator-wiki/). + + +## Project Setup +> [!WARNING] +> Make sure to initialize the [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules). +> +> You can either add the `--recurse-submodules` flag when doing `git clone` or +> do `git submodule update --init --recursive` once it is already cloned. + +This project uses [Bun](https://bun.sh) (for Web) and +[Deno](https://deno.com/) (for CLI). + +> [!IMPORTANT] +> Building the assembler dependency requires installing +> [rustup](https://rustup.rs/) and [Deno](https://deno.com/) + +```sh +bun install # install dependencies +bun dev:wasm # build wasm dependencies +``` + +### Compile Web and Hot-Reload for Development (with [Vite](https://vite.dev/)) +```sh +bun dev:web +``` + +### Run CLI and Hot-Reload for Development (with [Deno](https://deno.com/)) +```sh +bun dev:cli +``` + +> [!NOTE] +> Remember to pass the extra arguments, e.g: +> ```sh +> bun dev:cli -a ./architecture/RISCV/RV32IMFD.yml -I -c creatorconfig.yml +> ``` + + +### Building Web version for production +```sh +bun build:web +``` + +The resulting bundle will be saved to `dist/web/`. + +> [!TIP] +> To test locally the bundle version, as it will be deployed in GitHub Pages: +> ```bash +> REPO="creator" bun build:web +> cd dist/web +> python -m http.server 8080 +> ``` +> And go to [localhost:8080/creator/](https://localhost:8080/creator/) + + + +### Building CLI version +```sh +bun build:cli +``` + +### Lint with [ESLint](https://eslint.org/) + +```sh +bun lint +``` + +### Format with [Prettier](https://prettier.io/) + +```sh +bun format +``` + +### Run Tests + +Unit tests (with [Deno](https://deno.com/)) + +```sh +deno test -A --unstable-node-globals --parallel +``` + +Integration tests use [Deno's snapshot testing](https://docs.deno.com/examples/snapshot_test_tutorial/). +They store the last known good result, and compare new results against the +stored snapshots to verify them (showing the differences if they don't match). +For this reason, the snapshots should always be committed to the repo. They are +run along with the other tests using the command above. The snapshots can be +created/updated automatically with: + +```sh +deno test -A --unstable-node-globals --parallel -- --update +``` \ No newline at end of file diff --git a/docs/dev.md b/docs/dev.md deleted file mode 100644 index ede2329b7..000000000 --- a/docs/dev.md +++ /dev/null @@ -1,189 +0,0 @@ -# Developing CREATOR - -> [!WARNING] -> Make sure to initialize the [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules). -> -> You can either add the `--recurse-submodules` flag when doing `git clone` or -> do `git submodule update --init --recursive` once it is already cloned. - - - -## Project Setup -This project uses [Bun](https://bun.sh) (for Web) and -[Deno](https://deno.com/) (for CLI). - -> [!IMPORTANT] -> Building the assembler dependency requires installing -> [rustup](https://rustup.rs/) and [Deno](https://deno.com/) - -```sh -bun install # install dependencies -bun dev:wasm # build wasm dependencies -``` - -### Compile Web and Hot-Reload for Development (with [Vite](https://vite.dev/)) -```sh -bun dev:web -``` - -### Run CLI and Hot-Reload for Development (with [Deno](https://deno.com/)) -```sh -bun dev:cli -``` - -> [!NOTE] -> Remember to pass the extra arguments, e.g: -> ```sh -> bun dev:cli -a ./architecture/RISCV/RV32IMFD.yml -I -c creatorconfig.yml -> ``` - - -### Building Web version for production -```sh -bun build:web -``` - -The resulting bundle will be saved to `dist/web/`. - -> [!TIP] -> To test locally the bundle version, as it will be deployed in GitHub Pages: -> ```bash -> REPO="creator" bun build:web -> cd dist/web -> python -m http.server 8080 -> ``` -> And go to [localhost:8080/creator/](https://localhost:8080/creator/) - - - -### Building CLI version -```sh -bun build:cli -``` - -### Lint with [ESLint](https://eslint.org/) - -```sh -bun lint -``` - -### Format with [Prettier](https://prettier.io/) - -```sh -bun format -``` - -### Run Tests - -Unit tests (with [Deno](https://deno.com/)) - -```sh -deno test -A --unstable-node-globals --parallel -``` - -Integration tests use [Deno's snapshot testing](https://docs.deno.com/examples/snapshot_test_tutorial/). -They store the last known good result, and compare new results against the -stored snapshots to verify them (showing the differences if they don't match). -For this reason, the snapshots should always be committed to the repo. They are -run along with the other tests using the command above. The snapshots can be -created/updated automatically with: - -```sh -deno test -A --unstable-node-globals --parallel -- --update -``` - -## Backend RPC Server -This project includes a JSON RPC server that exposes the CREATOR emulator's core functionalities. - -For more details, see the [RPC Server README](src/rpc/README.md). - - - -## VS Code Setup -The recommended extensions are: -- [`Vue.volar`](https://marketplace.visualstudio.com/items?itemName=Vue.volar) -- [`denoland.vscode-deno`](https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno) -- [`dbaeumer.vscode-eslint`](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) -- [`esbenp.prettier-vscode`](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) -- [`redhat.vscode-yaml`](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) - -> [!NOTE] -> The recommended formatter to use with Vue files is Prettier (`esbenp.prettier-vscode`): -> ```json -> "[vue]": { -> "editor.defaultFormatter": "esbenp.prettier-vscode" -> }, -> ``` - -#### Debugging -We provide some example [launch configurations](https://code.visualstudio.com/docs/debugtest/debugging-configuration#_launch-configurations): - -##### Web -We'll need to [launch the application in DEV mode](#compile-and-hot-reload-for-development), and then attach the VS Code debugger to the Chrome instance. -```json -{ - "type": "chrome", - "request": "launch", - "name": "Debug Web", - "url": "http://localhost:5173", - "webRoot": "${workspaceFolder}" -} -``` - -##### CLI -```json -{ - "type": "node", - "request": "launch", - "name": "Debug CLI", - "program": "${workspaceFolder}/src/cli/creator-cli.mts", - "runtimeExecutable": "deno", - "console": "integratedTerminal", - "runtimeArgs": ["-A", "--unstable-node-globals", "--inspect-brk"], - "experimentalNetworking": "off", - "args": [ - "-a", - "./architecture/RISCV/RV32IMFD.yml", - "-s", - "./tests/arch/riscv/correct/examples/test_riscv_example_011.s", - "-c", - "creatorconfig.yml", - "-I" - ], - "attachSimplePort": 9229 -} -``` - - -## Resources - -### General -- [MDN Web Docs](https://developer.mozilla.org/) -- [The TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/intro.html) -- [D. Camarmas et al. - CREATOR: Simulador didáctico y genérico para la programación en ensamblador](https://zenodo.org/records/5130302) -- [D. Camarmas - Diseño y desarrollo de un simulador genérico para programación en ensamblador](https://e-archivo.uc3m.es/entities/publication/ccca4c06-de17-48f9-9ff8-04bc66c54a74) - - -### Web -- [Vue.js docs](https://vuejs.org/guide/) -- [BoostrapVueNext Docs](https://bootstrap-vue-next.github.io/bootstrap-vue-next/) & [Bootstrap Docs](https://getbootstrap.com/docs/) -- [Font Awesome Icons](https://fontawesome.com/search?ic=free) & [Font Awesome Docs](https://docs.fontawesome.com/) -- [Monaco Docs](https://microsoft.github.io/monaco-editor/docs.html), [monaco-vim](https://github.com/brijeshb42/monaco-vim) & [monaco-yaml](https://github.com/remcohaszing/monaco-yaml) -- [vue3-apexcharts](https://github.com/apexcharts/vue3-apexcharts) & [ApexCharts Docs](https://apexcharts.com/docs) -- [CSS Tricks](https://css-tricks.com/) -- [`vue-tricks.md`](docs/vue-tricks.md) - - -### Assembler -- [A. Guerrero - Desarrollo de un Compilador Genérico de Lenguaje Ensamblador para el Simulador CREATOR](https://github.com/ALVAROPING1/TFG) -- [The Rust Programming Language](https://doc.rust-lang.org/book/title-page.html) -- [Assembler Contribution Docs](https://github.com/creatorsim/creator-assembler/blob/master/CONTRIBUTING.md) -- [Serde (Deserialization Library) Docs](https://docs.rs/serde/) -- [Chumsky (Parser Library) Docs](https://docs.rs/chumsky) -- [Ariadne (Error Renderer Library) Docs](https://docs.rs/ariadne) diff --git a/docs/devices.md b/docs/devices.md deleted file mode 100644 index 896e23037..000000000 --- a/docs/devices.md +++ /dev/null @@ -1,67 +0,0 @@ -# Devices in CREATOR -Devices in CREATOR simulate the behavior of different components that the simulator can interact with, e.g. a terminal, a hard drive, etc. - -The code interacts with a device through the program memory, a device specifies a set of "special" memory addresses that can be read and written by both the code and the device. - -Devices are implemented in [`core/executor/devices.mts`](../src/core/executor/devices.mts). - - - -## Device definition -A device is defined by the following: -- An identifier (`id`), which uniquely identifies the device. -- A _control register address_ (`ctrl_addr`), which is typically used by the processor to signal an action to perform on the device. -- A _status register address_ (`status_addr`), which is typically used by the device to signal its status to the processor. -- A _data range_ (`data`), which defines a section of memory (`start` and `end`) that is shared between the processor and the device, typically for the exchange of data. -- A _handler function_ (`handler()`), which is called once per cycle, and defines the behavior of the device. -- An _enabled_ flag (`enabled`), which controls whether the device is enabled or not. If it's not, the callback is not called. - -To create a new device, create a new class extending `Device` and implementing its `handler` method. Then, instantiate that object and add it to `devices` with its corresponding ID. - - - -## Device handling -After executing each instruction, the executor calls `handleDevices()`, which executes the handlers for all enabled devices. - -Typically, a device reads its control register and checks its value. If it's `0`, it exits. If it's not, it works with the other data and clears the control register when it ends by calling `reset()`. - - - -## Implemented devices - -### `ConsoleDevice` -A device for interacting with CREATOR's console. - -Depending on the value stored in the control register (`ctrl_addr`), it executes one of the following: -- `1` - _print int_: Reads a word from the `data.start` address and writes it as an integer value in the console. -- `2` - _print float_: Reads a word from the `data.start` address and writes it as a float value in the console. -- `3` - _print double_: Reads a word from the `data.start` address and writes it as a float value in the console. -- `4` - _print string_: Reads the main memory address of a string from the `data.start` address and writes it in the console. -- `5` - _read int_: Reads an integer from the console and stores it as a word in `data.start`. -- `6` - _read float_: Reads a float from the console and stores it as a word in `data.start`. -- `7` - _read double_: Reads a double from the console and stores it as a word in `data.start`. -- `8` - _read string_: Reads a string from the console of the length specified in `data.start + 4` and stores it in the main memory address specified in `data.start`. -- `11` - _print char_: Reads a byte from the `data.start` address and writes it as a char in the console. -- `12` - _read char_: Reads a char from the console and stores it as a byte in `data.start`. - -#### Memory addresses -- `ctrl_addr`: `0xf0000000` -- `status_addr`: `0xf0000004` -- `data`: - - `start`: `0xf0000008` - - `end`: `0xf000000f` - - -### `OSDriver` -A device to simulate system calls to a fictitious "Operating System". - -Depending on the value stored in the control register (`ctrl_addr`), it executes one of the following: -- `9` - _sbrk_: Allocates a segment of main memory of the size specified in `data.start` and stores its address in `data.start`. -- `10` - _exit_: Terminates the current program's execution. - -#### Memory addresses -- `ctrl_addr`: `0xf0000010` -- `status_addr`: `0xf0000014` -- `data`: - - `start`: `0xf0000018` - - `end`: `0xf000001f` \ No newline at end of file diff --git a/docs/interrupts.md b/docs/interrupts.md deleted file mode 100644 index d420c525b..000000000 --- a/docs/interrupts.md +++ /dev/null @@ -1,119 +0,0 @@ -# Managing interrupts in CREATOR - -Interrupts are handled in the `execute_instruction()` function, after fetching the instruction. -Interrupts are marked as enabled through the `interruptsEnabled` flag in status, and checked through the `checkinterrupt()` function (which uses the architecture-defined `check`). - -When an interrupt is detected, and interrupts are enabled, the `handleInterrupt()` function is executed. -This function changes the execution mode to `ExecutionMode.Kernel`, stores the `program_counter` register in the `exception_program_counter` register, and jumps to the interruption handler address obtained through the architecture-defined `get_handler_addr`. -Finally, it clears the interruption through the architechitecture-defined `clear`. - -The execution mode change is required for the execution of privileged instructions (see [Privileged instructions in CREATOR](privileged.md)). - - -## Architecture definition -For interrupts to be managed correctly, the architecture definition file must include the following properties: -- `interrupts.enabled: boolean`: Controls whether interrupts are enabled by default. -- `interrupts.check: string`: JS code to be executed in order to check whether an interrupt happened. It must return a -`InterruptType` (if an interrupt happened) or `null` (if it didn't). -- `interrupts.is_enabled: string`: JS code to be executed in order to check whether interrupts are enabled. -- `interrupts.enable: string`: JS code to be executed in order to enable interrupts. -- `interrupts.disable: string`: JS code to be executed in order to disable interrupts. -- `interrupts.get_handler_addr: string`: JS code to be executed in order to obtain the interrupt handler address. -- `interrupts.clear: string`: JS code to be executed in order to clear an interrupt. -- `interrupts.create: (InterruptType) => null`: JS arrow (lambda) function to be executed in order to set an interrupt given an interrupt type. - - -## API - -### Functions -The following functions, belonging to -[`core/executor/interrupts.mts`](../src/core/executor/InterruptManager.mts), were implemented: -- `enableInterrupts(null) -> null`: Enables interrupts by calling `architecture.interrupts.enable`. -- `disableInterrupts(null) -> null`: Disables interrupts by calling `architecture.interrupts.disable`. -- `checkInterrupt(null) -> null`: Checks whether interrupts are enabled -- `handleInterrupt(null) -> null`: Handles an interrupt. - -### Enums -- `InterruptType` - - `Software` - - `Timer` - - `External` - - `EnvironmentCall` -- `ExecutionMode` - - `User` - - `Kernel` - - -### Variables -The variables that control the interrupts are stored in `core:status`: -- `status.interrupts_enabled: bool`: status of the interrupts. -- `status.execution_mode: ExecutionMode`: current execution mode. - - -## Example: Interrupts in RISC-V -An example of RISC-V with interruptions can be found in the -[`RISC_V_RV32IMFD_Interrupts.json`](../architecture/RISCV/RV32IMFD.yml) - - -### Interrupt mechanism -In RISC-V, when an interrupt happens, a bit is set in the `MIP` (_Machine Interrupt Pending_) control register. -Depending on the type of interrupt, it sets a different bit. -For example: -- Bit `3` (`MSIP`) is set to indicate a _software_ interrupt -- Bit `11` (`MEIP`) is set to indicate an _external_ interrupt - -Therefore, `check` must read these values in order to determine the type of the interrupt. - -Then, the value of the current instruction is stored in the `MEPC` control register (tagged as `exception_program_counter`). -The value for the interrupt handler is stored in the `MTVEC` control register, where bits `1` and `0` (MODE) determine the vector mode, and the rest of the register encodes the base address (BASE). -The different modes are: -- `0` (direct): All traps set `pc` to the base address -- `1` (vectored): Asynchronous interrupts set `pc` to $BASE+4\times cause$ - -Here we implemented the _direct_ mode, meaning that `MTVEC` holds `0x00000000`, the address of the handler. - -> [!NOTE] -> As we'll see in [Interrupt handling](#interrupt-handling), this requires the -> handling routine to be at the start of the text (`.text`) segment. - -Also, the cause of the interrupt is stored in the `MCAUSE` (_Machine Cause_). -This control register is divided into bit `31`, which holds the interrupt type, and the rest of the bits, each bit corresponding to a specific exception code. -Some of the most used are: -- `0`-`3` (`0x00000008`): Machine software interrupt -- `0`-`8` (`0x00000100`): Machine external interrupt - `1`-`11` (`0x80000800`): Environment call from U-mode - -Therefore, in the case of the `ecall` instruction, bit `3` of `MIP` and bit 8 of `MCAUSE` are set. - -### Interrupt enabling -The `MIE` control register is in charge, together with `MSTATUS`, of enabling/disabling interrupt types. The types use the same bits as in the `MIP` register. - - -### Interrupt handling -A full example of handling an environment call is provided in -[`examples/riscv-interrupts/ex0.s`](../examples/RISCV-32-interrupts/example1.s). - -First, we need to talk about some new privileged instructions: - -- `mret`: This instruction is used to return from an interrupt, which saves the `MEPC` to the `PC`, clears the interrupt by clearing bits `3` and `11` in `MIP`, and resetting `MCAUSE` to `0`. It also changes the execution mode back to `ExecutionMode.User` (U-mode) -- `csrrw`: This instruction switches the values of a control register and a user register. It's mainly used to store the values of user registers while handling the interrupt, as we can't operate with control registers. -The `MSCRATCH` control register is provided in order to add an extra register. - -Reference: [The RISC-V Instruction Set Manual Volume II: Privileged Architecture](https://github.com/riscv/riscv-isa-manual/), chapters 3.1, 3.3.1 and 3.3.2. - - -### Implemented features -Here is the table of implemented RISC-V features: - -| Chapter | Feature | Status | Notes | -| ----------------------------------------- | ------------------------------------------------------------------------- | :----------------: | ---------------------------------------------------------------------------------------------------------------------------- | -| I.7.1 | CSR Instructions | :white_check_mark: | Only `csrrw`, and without checking for register `x0` | -| II.3.1.1 - II.3.1.5 | Processor and ISA information (`misa`, `mvendorid`, etc.) | :x: | | -| II.3.1.6 | `mstatus`/`mstatush` | :white_check_mark: | Only _Privilege and Global Interrupt-Enable_ (chapter II.3.1.6.1). Only `mstatus`, as only the 32-bit version is implemented | -| II.3.1.7, II.3.1.9, II.3.1.13 - II.3.1.16 | Interrupts (`mtvec`, `mip`, `mie`, `mscratch`, `mepc`, `mcause`) | :white_check_mark: | No `mtval` | -| II.3.1.8 | Trap Delegation | :x: | | -| II.3.1.10 | Hardware performance Monitor | :x: | | -| II.3.1.11 - II.3.1.12 | Counters | :x: | | -| II.3.2.1 - II.3.3.2 | Environmen Calls and Trap-return | :white_check_mark: | Not breakpoints | -| II.3.1.17 - II.3.2, II.3.6 - II.3.7 | Environment, Security and Memory | :x: | | -| II.10 | Supervisor-Level ISA | :x: | | -| II.4 - II.9, II.11 - II.18 | Volume II Extensions | :x: | | diff --git a/docs/privileged.md b/docs/privileged.md deleted file mode 100644 index 879729385..000000000 --- a/docs/privileged.md +++ /dev/null @@ -1,7 +0,0 @@ -# Privileged instructions in CREATOR -A privileged instruction is an instruction that can only be executed in a privileged execution mode. In CREATOR, we define two execution modes: _user_ (non-privileged) and _kernel_ (privileged). - -An instruction is defined as privileged if it has the `privileged` property. - -The current execution mode is tracked by the `currentExecutionMode` variable, which is part of the _enum_ `ExecutionMode`, which can hold two values: `ExecutionMode.User` and `ExecutionMode.Kernel`. -This variable is initialized to `ExecutionMode.User`. \ No newline at end of file diff --git a/docs/schema/README.md b/docs/schema/README.md new file mode 100644 index 000000000..8984462b7 --- /dev/null +++ b/docs/schema/README.md @@ -0,0 +1,18 @@ +# CREATOR JSON Schemas + +Here are the [JSON schemas](https://json-schema.org/) for several of CREATOR's configuration files. + +## Architecture +This schema is _derived_ from the one generated by [creator-assembler](../../creator-assembler/). There are some differences, as the "input"/"external" YAML user configuration file that this schema represents is transformed into the "internal" JSON architecture definition file that the assembler uses. + +Therefore, if there are some changes made to the assembler's configuration file format, those must manually be reflected in [architecture.json](architecture.json). +The process is the following: +1. Generate the assembler JSON schema **before** the changes: + ```bash + cargo run -- schema > old-schema.json + ``` +2. Generate new schema: + ```bash + cargo run -- schema > new-schema.json + ``` +3. Diff the changes and apply them to [architecture.json](architecture.json). \ No newline at end of file