Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SC64][FW][SW] New framework for SC64 IRQ handling #68

Merged
merged 27 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
feb3ade
initial irq overhaul
Polprzewodnikowy Apr 10, 2024
dea9441
Merge branch 'main' into new-irq
Polprzewodnikowy Apr 11, 2024
e163ae8
Merge branch 'main' into new-irq
Polprzewodnikowy May 20, 2024
7a19097
keep legacy method of interrupt clear
Polprzewodnikowy May 20, 2024
31ea6e6
fw irq improvements (force high state for one clock when de-asserting…
Polprzewodnikowy May 21, 2024
7d8614e
handle irq in the bootloader
Polprzewodnikowy May 21, 2024
ad88fef
small cleanup
Polprzewodnikowy May 24, 2024
5cba981
set status register on reset
Polprzewodnikowy May 24, 2024
392ad5b
another cleanup
Polprzewodnikowy May 27, 2024
759df3b
interrupts disable macro
Polprzewodnikowy May 27, 2024
a6a223a
small message change
Polprzewodnikowy May 27, 2024
10454f4
another small cleanup
Polprzewodnikowy May 27, 2024
0d9e23b
Merge branch 'main' into new-irq
Polprzewodnikowy Jun 6, 2024
2a33bff
register PI reg bus
Polprzewodnikowy Jun 12, 2024
2b39606
Merge branch 'main' into new-irq
Polprzewodnikowy Jun 14, 2024
401e073
Merge branch 'main' into new-irq
Polprzewodnikowy Jul 21, 2024
8fd12e9
Merge branch 'main' into new-irq
Polprzewodnikowy Jul 21, 2024
080f4ce
Merge branch 'main' into new-irq
Polprzewodnikowy Aug 4, 2024
4187a5c
fix no error reporting
Polprzewodnikowy Aug 4, 2024
5b986f9
improve timings and build speed (multicore processing)
Polprzewodnikowy Aug 4, 2024
598a420
more IRQ sources + AUX data channel
Polprzewodnikowy Aug 5, 2024
9f9c3fc
shuffle some bits
Polprzewodnikowy Aug 9, 2024
5e07406
Merge branch 'main' into new-irq
Polprzewodnikowy Aug 11, 2024
8fb2254
better PI IO activity monitoring
Polprzewodnikowy Aug 15, 2024
02eed1c
documentation change
Polprzewodnikowy Aug 15, 2024
d630315
ping
Polprzewodnikowy Aug 15, 2024
dc0b049
little cleanup
Polprzewodnikowy Aug 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 128 additions & 47 deletions docs/01_memory_map.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
- [Internal memory map](#internal-memory-map)
- [PI memory map](#pi-memory-map)
- [Address decoding limitations](#address-decoding-limitations)
- [Flash mapped sections](#flash-mapped-sections)
- [Address decoding limitations](#address-decoding-limitations)
- [Flash mapped sections](#flash-mapped-sections)
- [SC64 registers](#sc64-registers)
- [`0x1FFF_0000`: **STATUS/COMMAND**](#0x1fff_0000-statuscommand)
- [`0x1FFF_0004`: **DATA0** and `0x1FFF_0008`: **DATA1**](#0x1fff_0004-data0-and-0x1fff_0008-data1)
- [`0x1FFF_0000`: **SCR**](#0x1fff_0000-scr)
- [`0x1FFF_0004`: **DATA0** / `0x1FFF_0008`: **DATA1**](#0x1fff_0004-data0--0x1fff_0008-data1)
- [`0x1FFF_000C`: **IDENTIFIER**](#0x1fff_000c-identifier)
- [`0x1FFF_0010`: **KEY**](#0x1fff_0010-key)
- [`0x1FFF_0014`: **IRQ**](#0x1fff_0014-irq)
- [`0x1FFF_0018`: **AUX**](#0x1fff_0018-aux)
- [Command execution flow](#command-execution-flow)
- [Without interrupt](#without-interrupt)
- [With interrupt](#with-interrupt)


---

## Internal memory map
# Internal memory map

This mapping is used internally by FPGA/μC and when accessing flashcart from USB side.

Expand All @@ -27,11 +31,11 @@ This mapping is used internally by FPGA/μC and when accessing flashcart from US

- Note [1]: Flash memory region `0x04E0_0000` - `0x04FD_FFFF` is write protected as it contains N64 bootloader. This section can be overwritten only via firmware update process.
- Note [2]: Due to BlockRAM usage optimization this section is read only.
- Note [3]: Read returns `0`. Maximum accessibe address space is 128 MiB.
- Note [3]: Read returns `0`. Maximum accessible address space is 128 MiB.


---

## PI memory map
# PI memory map

This mapping is used when accessing flashcart from N64 side.

Expand All @@ -51,11 +55,11 @@ This mapping is used when accessing flashcart from N64 side.
| EEPROM | `0x1FFE_2000` | 2 kiB | RW | `0x0500_2000` | Block RAM | mem bus | SC64 register access is enabled |
| 64DD buffer [8] | `0x1FFE_2800` | 256 bytes | RW | `0x0500_2800` | Block RAM | mem bus | SC64 register access is enabled |
| FlashRAM buffer [8] | `0x1FFE_2900` | 128 bytes | R | `0x0500_2900` | Block RAM | mem bus | SC64 register access is enabled |
| SC64 registers | `0x1FFF_0000` | 20 bytes | RW | N/A | Flashcart Interface | reg bus | SC64 register access is enabled |
| SC64 registers | `0x1FFF_0000` | 28 bytes | RW | N/A | Flashcart Interface | reg bus | SC64 register access is enabled |

- Note [1]: 64DD IPL share SDRAM memory space with ROM (last 4 MiB minus 128 kiB for saves). Write access is always disabled for this section.
- Note [2]: SRAM and FlashRAM save types share SDRAM memory space with ROM (last 128 kiB).
- Note [3]: 32 kiB chunks are accesed at `0x0800_0000`, `0x0804_0000` and `0x0808_0000`.
- Note [3]: 32 kiB chunks are accessed at `0x0800_0000`, `0x0804_0000` and `0x0808_0000`.
- Note [4]: FlashRAM read access is multiplexed between mem and reg bus, writes are always mapped to reg bus.
- Note [5]: Write access is available when `ROM_WRITE_ENABLE` config is enabled.
- Note [6]: This address overlaps last 128 kiB of ROM space allowing SRAM and FlashRAM save types to work with games occupying almost all of ROM space (for example Pokemon Stadium 2). Reads are redirected to last 128 kiB of flash.
Expand All @@ -80,79 +84,156 @@ Special commands are provided for performing flash erase and program.
During those operations avoid accessing flash mapped sections.
Data read will be corrupted and erase/program operations slows down.

---

## SC64 registers

SC64 contains small register region used for communication between N64 and controller code running on the μC.
Protocol is command based with support for up to 256 diferrent commands and two 32-bit argument/result values per operation.
Support for interrupts is provided but currently no command relies on it, 64DD IRQ is handled separately.
# SC64 registers

| name | address | size | access | usage |
| ------------------ | ------------- | ------- | ------ | ---------------------------------- |
| **STATUS/COMMAND** | `0x1FFF_0000` | 4 bytes | RW | Command execution and status |
| **DATA0** | `0x1FFF_0004` | 4 bytes | RW | Command argument/result 0 |
| **DATA1** | `0x1FFF_0008` | 4 bytes | RW | Command argument/result 1 |
| **IDENTIFIER** | `0x1FFF_000C` | 4 bytes | RW | Flashcart identifier and IRQ clear |
| **KEY** | `0x1FFF_0010` | 4 bytes | W | SC64 register access lock/unlock |
SC64 contains small register region used for communication between N64 and controller code running on the μC.
Protocol is command based with support for up to 256 different commands and two 32-bit argument/result values per operation.
Command execution finish can be optionally signaled with "cart interrupt"

| name | address | size | access | usage |
| -------------- | ------------- | ------- | ------ | -------------------------------- |
| **SCR** | `0x1FFF_0000` | 4 bytes | RW | Command execution and status |
| **DATA0** | `0x1FFF_0004` | 4 bytes | RW | Command argument/result 0 |
| **DATA1** | `0x1FFF_0008` | 4 bytes | RW | Command argument/result 1 |
| **IDENTIFIER** | `0x1FFF_000C` | 4 bytes | RW | Flashcart identifier |
| **KEY** | `0x1FFF_0010` | 4 bytes | W | SC64 register access lock/unlock |
| **IRQ** | `0x1FFF_0014` | 4 bytes | W | IRQ clear and enable |
| **AUX** | `0x1FFF_0018` | 4 bytes | RW | Auxiliary interrupt data channel |

---

#### `0x1FFF_0000`: **STATUS/COMMAND**

| name | bits | access | meaning |
| ------------- | ------ | ------ | ----------------------------------------------------- |
| `CMD_BUSY` | [31] | R | `1` if dispatched command is pending/executing |
| `CMD_ERROR` | [30] | R | `1` if last executed command returned with error code |
| `IRQ_PENDING` | [29] | R | `1` if flashcart has raised an interrupt |
| N/A | [28:8] | N/A | Unused, write `0` for future compatibility |
| `CMD_ID` | [7:0] | RW | Command ID to be executed |
### `0x1FFF_0000`: **SCR**

| name | bits | access | meaning |
| ----------------- | ------ | ------ | ------------------------------------------------------------ |
| `CMD_BUSY` | [31] | R | `1` if dispatched command is pending/executing |
| `CMD_ERROR` | [30] | R | `1` if last executed command returned with error code |
| `BTN_IRQ_PENDING` | [29] | R | `1` if flashcart has raised a "button pressed" interrupt |
| `BTN_IRQ_MASK` | [28] | R | `1` means "button pressed" interrupt is enabled (always `1`) |
| `CMD_IRQ_PENDING` | [27] | R | `1` if flashcart has raised a "command finish" interrupt |
| `CMD_IRQ_MASK` | [26] | R | `1` means "command finish" interrupt is enabled (always `1`) |
| `USB_IRQ_PENDING` | [25] | R | `1` if flashcart has raised an "USB not empty" interrupt |
| `USB_IRQ_MASK` | [24] | R | `1` means "USB not empty" interrupt is enabled |
| `AUX_IRQ_PENDING` | [23] | R | `1` if flashcart has raised an "AUX not empty" interrupt |
| `AUX_IRQ_MASK` | [22] | R | `1` means "AUX not empty" interrupt is enabled |
| N/A | [21:9] | N/A | Unused, write `0` for future compatibility |
| `CMD_IRQ_REQUEST` | [8] | RW | Raise cart interrupt signal when command finishes execution |
| `CMD_ID` | [7:0] | RW | Command ID to be executed |

Note: Write to this register raises `CMD_BUSY` bit and clears `CMD_ERROR` bit. Flashcart then will start executing provided command.

---

#### `0x1FFF_0004`: **DATA0** and `0x1FFF_0008`: **DATA1**
### `0x1FFF_0004`: **DATA0** / `0x1FFF_0008`: **DATA1**

| name | bits | access | meaning |
| --------- | ------ | ------ | ---------------------------------- |
| `DATA0/1` | [31:0] | RW | Command argument (W) or result (R) |

Note: Result is valid only when command has executed and `CMD_BUSY` bit is reset. When `CMD_ERROR` is set then **DATA0** register contains error code.
Note: Result is valid only when command has finished execution and `CMD_BUSY` bit is reset.
When `CMD_ERROR` is set then **DATA0** register contains error code.

---

#### `0x1FFF_000C`: **IDENTIFIER**
### `0x1FFF_000C`: **IDENTIFIER**

| name | bits | access | meaning |
| ------------ | ------ | ------ | ------------------------------------------------- |
| `IDENTIFIER` | [31:0] | RW | Flashcart identifier (ASCII `SCv2`) and IRQ clear |
| name | bits | access | meaning |
| ------------ | ------ | ------ | ----------------------------------- |
| `IDENTIFIER` | [31:0] | R | Flashcart identifier (ASCII `SCv2`) |

Note: Writing any value to this register will clear pending flashcart interrupt.

---

#### `0x1FFF_0010`: **KEY**
### `0x1FFF_0010`: **KEY**

| name | bits | access | meaning |
| ----- | ------ | ------ | ------------------------------------------ |
| `KEY` | [31:0] | W | Value to be put into lock/unlock sequencer |

Note: By default from cold boot (power on) or console reset (NMI) flashcart will disable access to SC64 specific memory regions.
**KEY** register is always enabled and listening for writes regardless of lock/unlock state.
To enable SC64 registers it is necesarry to provide sequence of values to this register.
To enable SC64 registers it is necessary to provide sequence of values to this register.
Value `0x00000000` will reset sequencer state.
Two consequentive writes of values `0x5F554E4C` and `0x4F434B5F` will unlock all SC64 registers if flashcart is in lock state.
Two consecutive writes of values `0x5F554E4C` and `0x4F434B5F` will unlock all SC64 registers if flashcart is in lock state.
Value `0xFFFFFFFF` will lock all SC64 registers if flashcart is in unlock state.

---

## Command execution flow
### `0x1FFF_0014`: **IRQ**

| name | bits | access | meaning |
| ----------------- | ------- | ------ | ---------------------------------------------- |
| `BTN_CLEAR` | [31] | W | Write `1` to clear "button pressed" interrupt |
| `CMD_CLEAR` | [30] | W | Write `1` to clear "command finish" interrupt |
| `USB_CLEAR` | [29] | W | Write `1` to clear "USB not empty" interrupt |
| `AUX_CLEAR` | [28] | W | Write `1` to clear "AUX not empty" interrupt |
| N/A | [27:12] | N/A | Unused, write `0` for future compatibility |
| `USB_IRQ_DISABLE` | [11] | W | Write `1` to disable "USB not empty" interrupt |
| `USB_IRQ_ENABLE` | [10] | W | Write `1` to enable "USB not empty" interrupt |
| `AUX_IRQ_DISABLE` | [9] | W | Write `1` to disable "AUX not empty" interrupt |
| `AUX_IRQ_ENABLE` | [8] | W | Write `1` to enable "AUX not empty" interrupt |
| N/A | [7:0] | N/A | Unused, write `0` for future compatibility |

Note: All interrupts are cleared and disabled when any of the following events occur:
- Hard reset
- NMI reset
- SC64 registers lock

SC64 interrupts are completely disabled when register access is not enabled.

---

### `0x1FFF_0018`: **AUX**

This register can be used as a very simple interface to the PC via USB.
Writing to this register generates an USB transfer with the contents of the written data.
New data available in the register are signaled via cart interrupt that needs to be enabled beforehand by setting `AUX_IRQ_ENABLE` bit in the **IRQ** register.
Status can be also manually polled by checking `AUX_IRQ_PENDING` bit in **SCR** register.
Interrupt needs to be acknowledged by setting `AUX_CLEAR` bit in the **IRQ** register.
There is no flow control, use this register as a ping-pong interface.
For example, PC sends AUX data, N64 receives interrupt, reads the data then writes to this register with a response.
This flow can be reversed if needed - N64 can be the initiating side.

| name | bits | access | meaning |
| ------ | ------ | ------ | -------------- |
| `DATA` | [31:0] | RW | Arbitrary data |

This register is used by the upload process in the `sc64deployer` to notify running app on the N64 about certain events.
All `DATA` values with upper 8 bits set to `1` (`0xFFxxxxxx`) are reserved for internal use by the SC64.
Refrain from using these values in your app for uses other than listed below.
Currently defined reserved `DATA` values are:

- `0xFF000000` - **Ping** - no-op command to test if app running on the N64 is listening to the AUX events.
- `0xFF000001` - **Halt** - causes the running app to stop all activity and wait in preparation for uploading new ROM to the SC64.
App still should listen to the AUX interrupt and respond to other messages.
- `0xFF000002` - **Reboot** - causes the running app to perform soft reboot by reloading IPL3 from the ROM and start executing it.

App running on the N64 shall respond to the AUX message with the same `DATA` value as incoming event for all events listed above, unless it's specified otherwise.



# Command execution flow

### Without interrupt

1. Check if command is already executing by reading `CMD_BUSY` bit in **SCR** register (optional).
2. Write command argument values to **DATA0** and **DATA1** registers, can be skipped if command doesn't require it.
3. Write command ID to **SCR** register.
4. Wait for `CMD_BUSY` bit in **SCR** register to go low.
5. Check if `CMD_ERROR` bit in **SCR** is set:
- If error is set then read **DATA0** register containing error code.
- If error is not set then read **DATA0** and **DATA1** registers containing command result values, can be skipped if command doesn't return any values.

### With interrupt

1. Check if command is already executing by reading `CMD_BUSY` bit in **STATUS/COMMAND** register (optional).
1. Check if command is already executing by reading `CMD_BUSY` bit in **SCR** register (optional).
2. Write command argument values to **DATA0** and **DATA1** registers, can be skipped if command doesn't require it.
3. Write command ID to **STATUS/COMMAND** register.
4. Wait for `CMD_BUSY` bit in **STATUS/COMMAND** register to go low.
5. Check if `CMD_ERROR` bit in **STATUS/COMMAND** is set:
3. Write command ID to **SCR** register and set `CMD_IRQ_REQUEST` bit high.
4. Wait for cart interrupt.
5. Check (in cart interrupt handler) if `CMD_IRQ_PENDING` bit in **SCR** register is set high.
6. Clear interrupt by setting `CMD_CLEAR` bit high in the **IRQ** register.
7. Check if `CMD_ERROR` bit in **SCR** is set:
- If error is set then read **DATA0** register containing error code.
- If error is not set then read **DATA0** and **DATA1** registers containing command result values, can be skipped if command doesn't return any values.
Loading