mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2025-01-13 04:59:09 +01:00
shuffle some bits
This commit is contained in:
parent
598a4205bb
commit
9f9c3fc19c
@ -1,21 +1,21 @@
|
|||||||
- [Internal memory map](#internal-memory-map)
|
- [Internal memory map](#internal-memory-map)
|
||||||
- [PI memory map](#pi-memory-map)
|
- [PI memory map](#pi-memory-map)
|
||||||
- [Address decoding limitations](#address-decoding-limitations)
|
- [Address decoding limitations](#address-decoding-limitations)
|
||||||
- [Flash mapped sections](#flash-mapped-sections)
|
- [Flash mapped sections](#flash-mapped-sections)
|
||||||
- [SC64 registers](#sc64-registers)
|
- [SC64 registers](#sc64-registers)
|
||||||
- [`0x1FFF_0000`: **STATUS/COMMAND**](#0x1fff_0000-statuscommand)
|
- [`0x1FFF_0000`: **SCR**](#0x1fff_0000-scr)
|
||||||
- [`0x1FFF_0004`: **DATA0** and `0x1FFF_0008`: **DATA1**](#0x1fff_0004-data0-and-0x1fff_0008-data1)
|
- [`0x1FFF_0004`: **DATA0** / `0x1FFF_0008`: **DATA1**](#0x1fff_0004-data0--0x1fff_0008-data1)
|
||||||
- [`0x1FFF_000C`: **IDENTIFIER**](#0x1fff_000c-identifier)
|
- [`0x1FFF_000C`: **IDENTIFIER**](#0x1fff_000c-identifier)
|
||||||
- [`0x1FFF_0010`: **KEY**](#0x1fff_0010-key)
|
- [`0x1FFF_0010`: **KEY**](#0x1fff_0010-key)
|
||||||
- [`0x1FFF_0014`: **IRQ**](#0x1fff_0014-irq)
|
- [`0x1FFF_0014`: **IRQ**](#0x1fff_0014-irq)
|
||||||
- [`0x1FFF_0018`: **AUX**](#0x1fff_0018-aux)
|
- [`0x1FFF_0018`: **AUX**](#0x1fff_0018-aux)
|
||||||
- [Command execution flow](#command-execution-flow)
|
- [Command execution flow](#command-execution-flow)
|
||||||
- [Without interrupt](#without-interrupt)
|
- [Without interrupt](#without-interrupt)
|
||||||
- [With interrupt](#with-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.
|
This mapping is used internally by FPGA/μC and when accessing flashcart from USB side.
|
||||||
|
|
||||||
@ -33,9 +33,9 @@ This mapping is used internally by FPGA/μC and when accessing flashcart from US
|
|||||||
- Note [2]: Due to BlockRAM usage optimization this section is read only.
|
- Note [2]: Due to BlockRAM usage optimization this section is read only.
|
||||||
- Note [3]: Read returns `0`. Maximum accessible 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.
|
This mapping is used when accessing flashcart from N64 side.
|
||||||
|
|
||||||
@ -84,55 +84,60 @@ Special commands are provided for performing flash erase and program.
|
|||||||
During those operations avoid accessing flash mapped sections.
|
During those operations avoid accessing flash mapped sections.
|
||||||
Data read will be corrupted and erase/program operations slows down.
|
Data read will be corrupted and erase/program operations slows down.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## SC64 registers
|
|
||||||
|
# SC64 registers
|
||||||
|
|
||||||
SC64 contains small register region used for communication between N64 and controller code running on the μC.
|
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.
|
Protocol is command based with support for up to 256 different 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.
|
Command execution finish can be optionally signaled with "cart interrupt"
|
||||||
|
|
||||||
| name | address | size | access | usage |
|
| name | address | size | access | usage |
|
||||||
| ------------------ | ------------- | ------- | ------ | -------------------------------- |
|
| -------------- | ------------- | ------- | ------ | -------------------------------- |
|
||||||
| **STATUS/COMMAND** | `0x1FFF_0000` | 4 bytes | RW | Command execution and status |
|
| **SCR** | `0x1FFF_0000` | 4 bytes | RW | Command execution and status |
|
||||||
| **DATA0** | `0x1FFF_0004` | 4 bytes | RW | Command argument/result 0 |
|
| **DATA0** | `0x1FFF_0004` | 4 bytes | RW | Command argument/result 0 |
|
||||||
| **DATA1** | `0x1FFF_0008` | 4 bytes | RW | Command argument/result 1 |
|
| **DATA1** | `0x1FFF_0008` | 4 bytes | RW | Command argument/result 1 |
|
||||||
| **IDENTIFIER** | `0x1FFF_000C` | 4 bytes | RW | Flashcart identifier |
|
| **IDENTIFIER** | `0x1FFF_000C` | 4 bytes | RW | Flashcart identifier |
|
||||||
| **KEY** | `0x1FFF_0010` | 4 bytes | W | SC64 register access lock/unlock |
|
| **KEY** | `0x1FFF_0010` | 4 bytes | W | SC64 register access lock/unlock |
|
||||||
| **IRQ** | `0x1FFF_0014` | 4 bytes | RW | IRQ clear and enable |
|
| **IRQ** | `0x1FFF_0014` | 4 bytes | W | IRQ clear and enable |
|
||||||
| **AUX** | `0x1FFF_0018` | 4 bytes | RW | Auxiliary interrupt data channel |
|
| **AUX** | `0x1FFF_0018` | 4 bytes | RW | Auxiliary interrupt data channel |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### `0x1FFF_0000`: **STATUS/COMMAND**
|
### `0x1FFF_0000`: **SCR**
|
||||||
|
|
||||||
| name | bits | access | meaning |
|
| name | bits | access | meaning |
|
||||||
| ----------------- | ------ | ------ | ----------------------------------------------------------- |
|
| ----------------- | ------ | ------ | ------------------------------------------------------------ |
|
||||||
| `CMD_BUSY` | [31] | R | `1` if dispatched command is pending/executing |
|
| `CMD_BUSY` | [31] | R | `1` if dispatched command is pending/executing |
|
||||||
| `CMD_ERROR` | [30] | R | `1` if last executed command returned with error code |
|
| `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_PENDING` | [29] | R | `1` if flashcart has raised a "button pressed" interrupt |
|
||||||
| `CMD_IRQ_PENDING` | [28] | R | `1` if flashcart has raised a "command finish" interrupt |
|
| `BTN_IRQ_MASK` | [28] | R | `1` means "button pressed" interrupt is enabled (always `1`) |
|
||||||
| `USB_IRQ_PENDING` | [27] | R | `1` if flashcart has raised au "USB not empty" interrupt |
|
| `CMD_IRQ_PENDING` | [27] | R | `1` if flashcart has raised a "command finish" interrupt |
|
||||||
| `AUX_IRQ_PENDING` | [26] | R | `1` if flashcart has raised an "AUX not empty" interrupt |
|
| `CMD_IRQ_MASK` | [26] | R | `1` means "command finish" interrupt is enabled (always `1`) |
|
||||||
| N/A | [25:9] | N/A | Unused, write `0` for future compatibility |
|
| `USB_IRQ_PENDING` | [25] | R | `1` if flashcart has raised an "USB not empty" interrupt |
|
||||||
| `CMD_IRQ_REQUEST` | [8] | RW | Raise cart interrupt signal when command finishes execution |
|
| `USB_IRQ_MASK` | [24] | R | `1` means "USB not empty" interrupt is enabled |
|
||||||
| `CMD_ID` | [7:0] | RW | Command ID to be executed |
|
| `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.
|
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 |
|
| name | bits | access | meaning |
|
||||||
| --------- | ------ | ------ | ---------------------------------- |
|
| --------- | ------ | ------ | ---------------------------------- |
|
||||||
| `DATA0/1` | [31:0] | RW | Command argument (W) or result (R) |
|
| `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 |
|
| name | bits | access | meaning |
|
||||||
| ------------ | ------ | ------ | ----------------------------------- |
|
| ------------ | ------ | ------ | ----------------------------------- |
|
||||||
@ -141,7 +146,7 @@ Note: Result is valid only when command has executed and `CMD_BUSY` bit is reset
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### `0x1FFF_0010`: **KEY**
|
### `0x1FFF_0010`: **KEY**
|
||||||
|
|
||||||
| name | bits | access | meaning |
|
| name | bits | access | meaning |
|
||||||
| ----- | ------ | ------ | ------------------------------------------ |
|
| ----- | ------ | ------ | ------------------------------------------ |
|
||||||
@ -156,40 +161,37 @@ Value `0xFFFFFFFF` will lock all SC64 registers if flashcart is in unlock state.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### `0x1FFF_0014`: **IRQ**
|
### `0x1FFF_0014`: **IRQ**
|
||||||
|
|
||||||
| name | bits | access | meaning |
|
| name | bits | access | meaning |
|
||||||
| ----------------- | ------- | ------ | ----------------------------------------------------------------- |
|
| ----------------- | ------- | ------ | ---------------------------------------------- |
|
||||||
| `BTN_CLEAR` | [31] | W | Write `1` to clear "button pressed" interrupt |
|
| `BTN_CLEAR` | [31] | W | Write `1` to clear "button pressed" interrupt |
|
||||||
| `CMD_CLEAR` | [30] | W | Write `1` to clear "command finish" interrupt |
|
| `CMD_CLEAR` | [30] | W | Write `1` to clear "command finish" interrupt |
|
||||||
| `USB_CLEAR` | [29] | W | Write `1` to clear "USB not empty" 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 |
|
| `AUX_CLEAR` | [28] | W | Write `1` to clear "AUX not empty" interrupt |
|
||||||
| N/A | [27:24] | N/A | Unused, write `0` for future compatibility |
|
| N/A | [27:12] | N/A | Unused, write `0` for future compatibility |
|
||||||
| `MCU_IRQ_MASK` | [23] | R | `1` means "button pressed" interrupt is enabled (it's always `1`) |
|
| `USB_IRQ_DISABLE` | [11] | W | Write `1` to disable "USB not empty" interrupt |
|
||||||
| `CMD_IRQ_MASK` | [22] | R | `1` means "command finish" interrupt is enabled (it's always `1`) |
|
| `USB_IRQ_ENABLE` | [10] | W | Write `1` to enable "USB not empty" interrupt |
|
||||||
| `USB_IRQ_MASK` | [21] | R | `1` means "USB not empty" interrupt is enabled |
|
| `AUX_IRQ_DISABLE` | [9] | W | Write `1` to disable "AUX not empty" interrupt |
|
||||||
| `AUX_IRQ_MASK` | [20] | R | `1` means "AUX not empty" interrupt is enabled |
|
| `AUX_IRQ_ENABLE` | [8] | W | Write `1` to enable "AUX not empty" interrupt |
|
||||||
| N/A | [19:16] | N/A | Unused, write `0` for future compatibility |
|
| N/A | [7:0] | 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:
|
Note: All interrupts are cleared and disabled when any of the following events occur:
|
||||||
- Hard reset
|
- Hard reset
|
||||||
- NMI reset
|
- NMI reset
|
||||||
- SC64 registers locking
|
- SC64 registers lock
|
||||||
|
|
||||||
SC64 interrupts are completely disabled when register access is not enabled.
|
SC64 interrupts are completely disabled when register access is not enabled.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### `0x1FFF_0018`: **AUX**
|
### `0x1FFF_0018`: **AUX**
|
||||||
|
|
||||||
This register can be used as a very simple interface to the PC via USB.
|
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.
|
Writing to this register generates an USB transfer with the contents of the written data.
|
||||||
New data available in the register is signaled via cart interrupt that needs to be enabled beforehand in the `IRQ` register.
|
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.
|
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.
|
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.
|
This flow can be reversed if needed - N64 can be the initiating side.
|
||||||
@ -198,28 +200,39 @@ This flow can be reversed if needed - N64 can be the initiating side.
|
|||||||
| ------ | ------ | ------ | -------------- |
|
| ------ | ------ | ------ | -------------- |
|
||||||
| `DATA` | [31:0] | RW | Arbitrary data |
|
| `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:
|
||||||
|
|
||||||
## Command execution flow
|
- `0xFF000001` - **IO Halt** - causes the running app to stop all cartridge IO activity (PI bus and Joybus) 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 reset 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
|
### Without 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.
|
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.
|
3. Write command ID to **SCR** register.
|
||||||
4. Wait for `CMD_BUSY` bit in **STATUS/COMMAND** register to go low.
|
4. Wait for `CMD_BUSY` bit in **SCR** register to go low.
|
||||||
5. Check if `CMD_ERROR` bit in **STATUS/COMMAND** is set:
|
5. Check if `CMD_ERROR` bit in **SCR** is set:
|
||||||
- If error is set then read **DATA0** register containing error code.
|
- 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.
|
- 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
|
### 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.
|
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 and set `CMD_IRQ_REQUEST` bit high.
|
3. Write command ID to **SCR** register and set `CMD_IRQ_REQUEST` bit high.
|
||||||
4. Wait for cart interrupt.
|
4. Wait for cart interrupt.
|
||||||
5. Check (in cart interrupt handler) if `CMD_IRQ_PENDING` bit in **STATUS/COMMAND** register is set high.
|
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.
|
6. Clear interrupt by setting `CMD_CLEAR` bit high in the **IRQ** register.
|
||||||
7. Check if `CMD_ERROR` bit in **STATUS/COMMAND** is set:
|
7. Check if `CMD_ERROR` bit in **SCR** is set:
|
||||||
- If error is set then read **DATA0** register containing error code.
|
- 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.
|
- 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.
|
||||||
|
@ -34,18 +34,25 @@ module n64_cfg (
|
|||||||
logic usb_irq;
|
logic usb_irq;
|
||||||
logic aux_irq;
|
logic aux_irq;
|
||||||
|
|
||||||
logic usb_irq_enabled;
|
logic btn_irq_mask;
|
||||||
logic aux_irq_enabled;
|
logic cmd_irq_mask;
|
||||||
|
logic usb_irq_mask;
|
||||||
|
logic aux_irq_mask;
|
||||||
|
|
||||||
always_ff @(posedge clk) begin
|
always_ff @(posedge clk) begin
|
||||||
irq <= (
|
irq <= (
|
||||||
cmd_irq ||
|
(btn_irq && btn_irq_mask) ||
|
||||||
btn_irq ||
|
(cmd_irq && cmd_irq_mask) ||
|
||||||
(usb_irq_enabled && usb_irq) ||
|
(usb_irq && usb_irq_mask) ||
|
||||||
(aux_irq_enabled && aux_irq)
|
(aux_irq && aux_irq_mask)
|
||||||
);
|
);
|
||||||
end
|
end
|
||||||
|
|
||||||
|
always_comb begin
|
||||||
|
btn_irq_mask = 1'b1;
|
||||||
|
cmd_irq_mask = 1'b1;
|
||||||
|
end
|
||||||
|
|
||||||
always_comb begin
|
always_comb begin
|
||||||
reg_bus.rdata = 16'd0;
|
reg_bus.rdata = 16'd0;
|
||||||
if (reg_bus.address[16] && (reg_bus.address[15:5] == 11'd0)) begin
|
if (reg_bus.address[16] && (reg_bus.address[15:5] == 11'd0)) begin
|
||||||
@ -54,10 +61,14 @@ module n64_cfg (
|
|||||||
n64_scb.cfg_pending,
|
n64_scb.cfg_pending,
|
||||||
cmd_error,
|
cmd_error,
|
||||||
btn_irq,
|
btn_irq,
|
||||||
|
btn_irq_mask,
|
||||||
cmd_irq,
|
cmd_irq,
|
||||||
|
cmd_irq_mask,
|
||||||
usb_irq,
|
usb_irq,
|
||||||
|
usb_irq_mask,
|
||||||
aux_irq,
|
aux_irq,
|
||||||
10'd0
|
aux_irq_mask,
|
||||||
|
6'd0
|
||||||
};
|
};
|
||||||
REG_COMMAND: reg_bus.rdata = {7'd0, cmd_irq_request, n64_scb.cfg_cmd};
|
REG_COMMAND: reg_bus.rdata = {7'd0, cmd_irq_request, n64_scb.cfg_cmd};
|
||||||
REG_DATA_0_H: reg_bus.rdata = n64_scb.cfg_wdata[0][31:16];
|
REG_DATA_0_H: reg_bus.rdata = n64_scb.cfg_wdata[0][31:16];
|
||||||
@ -68,14 +79,7 @@ module n64_cfg (
|
|||||||
REG_IDENTIFIER_L: reg_bus.rdata = n64_scb.cfg_identifier[15:0];
|
REG_IDENTIFIER_L: reg_bus.rdata = n64_scb.cfg_identifier[15:0];
|
||||||
REG_KEY_H: reg_bus.rdata = 16'd0;
|
REG_KEY_H: reg_bus.rdata = 16'd0;
|
||||||
REG_KEY_L: reg_bus.rdata = 16'd0;
|
REG_KEY_L: reg_bus.rdata = 16'd0;
|
||||||
REG_IRQ_H: reg_bus.rdata = {
|
REG_IRQ_H: reg_bus.rdata = 16'd0;
|
||||||
8'd0,
|
|
||||||
1'b1,
|
|
||||||
1'b1,
|
|
||||||
usb_irq_enabled,
|
|
||||||
aux_irq_enabled,
|
|
||||||
4'd0
|
|
||||||
};
|
|
||||||
REG_IRQ_L: reg_bus.rdata = 16'd0;
|
REG_IRQ_L: reg_bus.rdata = 16'd0;
|
||||||
REG_AUX_H: reg_bus.rdata = n64_scb.aux_wdata[31:16];
|
REG_AUX_H: reg_bus.rdata = n64_scb.aux_wdata[31:16];
|
||||||
REG_AUX_L: reg_bus.rdata = n64_scb.aux_wdata[15:0];
|
REG_AUX_L: reg_bus.rdata = n64_scb.aux_wdata[15:0];
|
||||||
@ -119,22 +123,22 @@ module n64_cfg (
|
|||||||
n64_scb.cfg_cmd <= 8'h00;
|
n64_scb.cfg_cmd <= 8'h00;
|
||||||
cmd_error <= 1'b0;
|
cmd_error <= 1'b0;
|
||||||
cmd_irq_request <= 1'b0;
|
cmd_irq_request <= 1'b0;
|
||||||
cmd_irq <= 1'b0;
|
|
||||||
btn_irq <= 1'b0;
|
btn_irq <= 1'b0;
|
||||||
|
cmd_irq <= 1'b0;
|
||||||
usb_irq <= 1'b0;
|
usb_irq <= 1'b0;
|
||||||
aux_irq <= 1'b0;
|
aux_irq <= 1'b0;
|
||||||
usb_irq_enabled <= 1'b0;
|
usb_irq_mask <= 1'b0;
|
||||||
aux_irq_enabled <= 1'b0;
|
aux_irq_mask <= 1'b0;
|
||||||
lock_sequence_counter <= 1'd0;
|
lock_sequence_counter <= 1'd0;
|
||||||
end else if (n64_scb.n64_reset || n64_scb.n64_nmi) begin
|
end else if (n64_scb.n64_reset || n64_scb.n64_nmi) begin
|
||||||
n64_scb.cfg_unlock <= 1'b0;
|
n64_scb.cfg_unlock <= 1'b0;
|
||||||
cmd_irq_request <= 1'b0;
|
cmd_irq_request <= 1'b0;
|
||||||
cmd_irq <= 1'b0;
|
|
||||||
btn_irq <= 1'b0;
|
btn_irq <= 1'b0;
|
||||||
|
cmd_irq <= 1'b0;
|
||||||
usb_irq <= 1'b0;
|
usb_irq <= 1'b0;
|
||||||
aux_irq <= 1'b0;
|
aux_irq <= 1'b0;
|
||||||
usb_irq_enabled <= 1'b0;
|
usb_irq_mask <= 1'b0;
|
||||||
aux_irq_enabled <= 1'b0;
|
aux_irq_mask <= 1'b0;
|
||||||
lock_sequence_counter <= 1'd0;
|
lock_sequence_counter <= 1'd0;
|
||||||
end else if (n64_scb.cfg_unlock) begin
|
end else if (n64_scb.cfg_unlock) begin
|
||||||
if (reg_bus.write && reg_bus.address[16] && (reg_bus.address[15:5] == 11'd0)) begin
|
if (reg_bus.write && reg_bus.address[16] && (reg_bus.address[15:5] == 11'd0)) begin
|
||||||
@ -185,12 +189,12 @@ module n64_cfg (
|
|||||||
if (reg_bus.wdata == 16'hFFFF) begin
|
if (reg_bus.wdata == 16'hFFFF) begin
|
||||||
n64_scb.cfg_unlock <= 1'b0;
|
n64_scb.cfg_unlock <= 1'b0;
|
||||||
cmd_irq_request <= 1'b0;
|
cmd_irq_request <= 1'b0;
|
||||||
cmd_irq <= 1'b0;
|
|
||||||
btn_irq <= 1'b0;
|
btn_irq <= 1'b0;
|
||||||
|
cmd_irq <= 1'b0;
|
||||||
usb_irq <= 1'b0;
|
usb_irq <= 1'b0;
|
||||||
aux_irq <= 1'b0;
|
aux_irq <= 1'b0;
|
||||||
usb_irq_enabled <= 1'b0;
|
usb_irq_mask <= 1'b0;
|
||||||
aux_irq_enabled <= 1'b0;
|
aux_irq_mask <= 1'b0;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -203,17 +207,18 @@ module n64_cfg (
|
|||||||
end
|
end
|
||||||
|
|
||||||
REG_IRQ_L: begin
|
REG_IRQ_L: begin
|
||||||
if (reg_bus.wdata[11]) begin
|
|
||||||
usb_irq_enabled <= 1'b0;
|
|
||||||
end
|
|
||||||
if (reg_bus.wdata[10]) begin
|
if (reg_bus.wdata[10]) begin
|
||||||
usb_irq_enabled <= 1'b1;
|
usb_irq_mask <= 1'b1;
|
||||||
|
end
|
||||||
|
if (reg_bus.wdata[11]) begin
|
||||||
|
usb_irq_mask <= 1'b0;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (reg_bus.wdata[8]) begin
|
||||||
|
aux_irq_mask <= 1'b1;
|
||||||
end
|
end
|
||||||
if (reg_bus.wdata[9]) begin
|
if (reg_bus.wdata[9]) begin
|
||||||
aux_irq_enabled <= 1'b0;
|
aux_irq_mask <= 1'b0;
|
||||||
end
|
|
||||||
if (reg_bus.wdata[8]) begin
|
|
||||||
aux_irq_enabled <= 1'b1;
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
io32_t SR_CMD;
|
io32_t SCR;
|
||||||
io32_t DATA[2];
|
io32_t DATA[2];
|
||||||
io32_t IDENTIFIER;
|
io32_t IDENTIFIER;
|
||||||
io32_t KEY;
|
io32_t KEY;
|
||||||
@ -16,13 +16,17 @@ typedef struct {
|
|||||||
#define SC64_REGS ((sc64_regs_t *) SC64_REGS_BASE)
|
#define SC64_REGS ((sc64_regs_t *) SC64_REGS_BASE)
|
||||||
|
|
||||||
|
|
||||||
#define SC64_SR_CMD_IRQ_REQUEST (1 << 8)
|
#define SC64_SCR_CPU_BUSY (1 << 31)
|
||||||
#define SC64_SR_AUX_IRQ_PENDING (1 << 26)
|
#define SC64_SCR_CMD_ERROR (1 << 30)
|
||||||
#define SC64_SR_USB_IRQ_PENDING (1 << 27)
|
#define SC64_SCR_BTN_IRQ_PENDING (1 << 29)
|
||||||
#define SC64_SR_CMD_IRQ_PENDING (1 << 28)
|
#define SC64_SCR_BTN_IRQ_MASK (1 << 28)
|
||||||
#define SC64_SR_BTN_IRQ_PENDING (1 << 29)
|
#define SC64_SCR_CMD_IRQ_PENDING (1 << 27)
|
||||||
#define SC64_SR_CMD_ERROR (1 << 30)
|
#define SC64_SCR_CMD_IRQ_MASK (1 << 26)
|
||||||
#define SC64_SR_CPU_BUSY (1 << 31)
|
#define SC64_SCR_USB_IRQ_PENDING (1 << 25)
|
||||||
|
#define SC64_SCR_USB_IRQ_MASK (1 << 24)
|
||||||
|
#define SC64_SCR_AUX_IRQ_PENDING (1 << 23)
|
||||||
|
#define SC64_SCR_AUX_IRQ_MASK (1 << 22)
|
||||||
|
#define SC64_SCR_CMD_IRQ_REQUEST (1 << 8)
|
||||||
|
|
||||||
#define SC64_V2_IDENTIFIER (0x53437632)
|
#define SC64_V2_IDENTIFIER (0x53437632)
|
||||||
|
|
||||||
@ -31,20 +35,14 @@ typedef struct {
|
|||||||
#define SC64_KEY_UNLOCK_2 (0x4F434B5FUL)
|
#define SC64_KEY_UNLOCK_2 (0x4F434B5FUL)
|
||||||
#define SC64_KEY_LOCK (0xFFFFFFFFUL)
|
#define SC64_KEY_LOCK (0xFFFFFFFFUL)
|
||||||
|
|
||||||
#define SC64_IRQ_AUX_ENABLE (1 << 8)
|
|
||||||
#define SC64_IRQ_AUX_DISABLE (1 << 9)
|
|
||||||
#define SC64_IRQ_USB_ENABLE (1 << 10)
|
|
||||||
#define SC64_IRQ_USB_DISABLE (1 << 11)
|
|
||||||
|
|
||||||
#define SC64_IRQ_AUX_MASK (1 << 20)
|
|
||||||
#define SC64_IRQ_USB_MASK (1 << 21)
|
|
||||||
#define SC64_IRQ_CMD_MASK (1 << 22)
|
|
||||||
#define SC64_IRQ_BTN_MASK (1 << 23)
|
|
||||||
|
|
||||||
#define SC64_IRQ_AUX_CLEAR (1 << 28)
|
|
||||||
#define SC64_IRQ_USB_CLEAR (1 << 29)
|
|
||||||
#define SC64_IRQ_CMD_CLEAR (1 << 30)
|
|
||||||
#define SC64_IRQ_BTN_CLEAR (1 << 31)
|
#define SC64_IRQ_BTN_CLEAR (1 << 31)
|
||||||
|
#define SC64_IRQ_CMD_CLEAR (1 << 30)
|
||||||
|
#define SC64_IRQ_USB_CLEAR (1 << 29)
|
||||||
|
#define SC64_IRQ_AUX_CLEAR (1 << 28)
|
||||||
|
#define SC64_IRQ_USB_DISABLE (1 << 11)
|
||||||
|
#define SC64_IRQ_USB_ENABLE (1 << 10)
|
||||||
|
#define SC64_IRQ_AUX_DISABLE (1 << 9)
|
||||||
|
#define SC64_IRQ_AUX_ENABLE (1 << 8)
|
||||||
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -101,20 +99,20 @@ static sc64_error_t sc64_execute_cmd (sc64_cmd_t *cmd) {
|
|||||||
|
|
||||||
if (use_cmd_irq) {
|
if (use_cmd_irq) {
|
||||||
wait_cmd_irq = true;
|
wait_cmd_irq = true;
|
||||||
pi_io_write(&SC64_REGS->SR_CMD, (SC64_SR_CMD_IRQ_REQUEST | (cmd->id & 0xFF)));
|
pi_io_write(&SC64_REGS->SCR, (SC64_SCR_CMD_IRQ_REQUEST | (cmd->id & 0xFF)));
|
||||||
while (wait_cmd_irq);
|
while (wait_cmd_irq);
|
||||||
sr = pi_io_read(&SC64_REGS->SR_CMD);
|
sr = pi_io_read(&SC64_REGS->SCR);
|
||||||
if (sr & SC64_SR_CPU_BUSY) {
|
if (sr & SC64_SCR_CPU_BUSY) {
|
||||||
error_display("[Unexpected] SC64 CMD busy flag set");
|
error_display("[Unexpected] SC64 CMD busy flag set");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pi_io_write(&SC64_REGS->SR_CMD, (cmd->id & 0xFF));
|
pi_io_write(&SC64_REGS->SCR, (cmd->id & 0xFF));
|
||||||
do {
|
do {
|
||||||
sr = pi_io_read(&SC64_REGS->SR_CMD);
|
sr = pi_io_read(&SC64_REGS->SCR);
|
||||||
} while (sr & SC64_SR_CPU_BUSY);
|
} while (sr & SC64_SCR_CPU_BUSY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sr & SC64_SR_CMD_ERROR) {
|
if (sr & SC64_SCR_CMD_ERROR) {
|
||||||
return (sc64_error_t) (pi_io_read(&SC64_REGS->DATA[0]));
|
return (sc64_error_t) (pi_io_read(&SC64_REGS->DATA[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +216,7 @@ void sc64_lock (void) {
|
|||||||
bool sc64_check_presence (void) {
|
bool sc64_check_presence (void) {
|
||||||
bool detected = (pi_io_read(&SC64_REGS->IDENTIFIER) == SC64_V2_IDENTIFIER);
|
bool detected = (pi_io_read(&SC64_REGS->IDENTIFIER) == SC64_V2_IDENTIFIER);
|
||||||
if (detected) {
|
if (detected) {
|
||||||
while (pi_io_read(&SC64_REGS->SR_CMD) & SC64_SR_CPU_BUSY);
|
while (pi_io_read(&SC64_REGS->SCR) & SC64_SCR_CPU_BUSY);
|
||||||
}
|
}
|
||||||
return detected;
|
return detected;
|
||||||
}
|
}
|
||||||
@ -237,18 +235,18 @@ void sc64_aux_irq_enable (bool enable) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sc64_irq_t sc64_irq_pending (void) {
|
sc64_irq_t sc64_irq_pending (void) {
|
||||||
uint32_t sr = pi_io_read(&SC64_REGS->SR_CMD);
|
uint32_t sr = pi_io_read(&SC64_REGS->SCR);
|
||||||
sc64_irq_t irq = SC64_IRQ_NONE;
|
sc64_irq_t irq = SC64_IRQ_NONE;
|
||||||
if (sr & SC64_SR_BTN_IRQ_PENDING) {
|
if ((sr & SC64_SCR_BTN_IRQ_MASK) && (sr & SC64_SCR_BTN_IRQ_PENDING)) {
|
||||||
irq |= SC64_IRQ_BTN;
|
irq |= SC64_IRQ_BTN;
|
||||||
}
|
}
|
||||||
if (sr & SC64_SR_CMD_IRQ_PENDING) {
|
if ((sr & SC64_SCR_CMD_IRQ_MASK) && (sr & SC64_SCR_CMD_IRQ_PENDING)) {
|
||||||
irq |= SC64_IRQ_CMD;
|
irq |= SC64_IRQ_CMD;
|
||||||
}
|
}
|
||||||
if (sr & SC64_SR_USB_IRQ_PENDING) {
|
if ((sr & SC64_SCR_USB_IRQ_MASK) && (sr & SC64_SCR_USB_IRQ_PENDING)) {
|
||||||
irq |= SC64_IRQ_USB;
|
irq |= SC64_IRQ_USB;
|
||||||
}
|
}
|
||||||
if (sr & SC64_SR_AUX_IRQ_PENDING) {
|
if ((sr & SC64_SCR_AUX_IRQ_MASK) && (sr & SC64_SCR_AUX_IRQ_PENDING)) {
|
||||||
irq |= SC64_IRQ_AUX;
|
irq |= SC64_IRQ_AUX;
|
||||||
}
|
}
|
||||||
return irq;
|
return irq;
|
||||||
|
@ -140,6 +140,10 @@ struct _64DDArgs {
|
|||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
rom: Option<PathBuf>,
|
rom: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Attempt to reboot the console (requires specific support in the running game)
|
||||||
|
#[arg(short, long)]
|
||||||
|
reboot: bool,
|
||||||
|
|
||||||
/// Path to the save file (also used by save writeback mechanism)
|
/// Path to the save file (also used by save writeback mechanism)
|
||||||
#[arg(short, long, requires = "rom")]
|
#[arg(short, long, requires = "rom")]
|
||||||
save: Option<PathBuf>,
|
save: Option<PathBuf>,
|
||||||
@ -367,27 +371,13 @@ fn handle_list_command() -> Result<(), sc64::Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_upload_command(connection: Connection, args: &UploadArgs) -> Result<(), sc64::Error> {
|
fn handle_upload_command(connection: Connection, args: &UploadArgs) -> Result<(), sc64::Error> {
|
||||||
const AUX_TOKEN_UPLOAD_START: u32 = 0xFF000001;
|
|
||||||
const AUX_TOKEN_REBOOT: u32 = 0xFF000002;
|
|
||||||
|
|
||||||
let mut sc64 = init_sc64(connection, true)?;
|
let mut sc64 = init_sc64(connection, true)?;
|
||||||
|
|
||||||
if args.reboot {
|
if args.reboot && !sc64.aux_try_notify(sc64::AuxMessage::IOHalt)? {
|
||||||
match sc64.aux_send_and_receive(
|
println!(
|
||||||
AUX_TOKEN_UPLOAD_START,
|
"{}",
|
||||||
std::time::Duration::from_millis(500),
|
"Warning: no response for [IOHalt] AUX message".bright_yellow()
|
||||||
)? {
|
);
|
||||||
Some(data) => println!(
|
|
||||||
"{}",
|
|
||||||
format!("N64 reboot prepare successful (0x{data:08X})")
|
|
||||||
.bright_green()
|
|
||||||
.bold()
|
|
||||||
),
|
|
||||||
None => println!(
|
|
||||||
"{}",
|
|
||||||
"N64 reboot prepare unsuccessful".bright_yellow().bold()
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sc64.reset_state()?;
|
sc64.reset_state()?;
|
||||||
@ -435,8 +425,11 @@ fn handle_upload_command(connection: Connection, args: &UploadArgs) -> Result<()
|
|||||||
|
|
||||||
sc64.calculate_cic_parameters(args.cic_seed)?;
|
sc64.calculate_cic_parameters(args.cic_seed)?;
|
||||||
|
|
||||||
if args.reboot {
|
if args.reboot && !sc64.aux_try_notify(sc64::AuxMessage::Reboot)? {
|
||||||
sc64.aux_send(AUX_TOKEN_REBOOT)?;
|
println!(
|
||||||
|
"{}",
|
||||||
|
"Warning: no response for [Reboot] AUX message".bright_yellow()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -477,6 +470,13 @@ fn handle_64dd_command(connection: Connection, args: &_64DDArgs) -> Result<(), s
|
|||||||
.bright_green()
|
.bright_green()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if args.reboot && !sc64.aux_try_notify(sc64::AuxMessage::IOHalt)? {
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
"Warning: no response for [IOHalt] AUX message".bright_yellow()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
sc64.reset_state()?;
|
sc64.reset_state()?;
|
||||||
|
|
||||||
if let Some(rom) = &args.rom {
|
if let Some(rom) = &args.rom {
|
||||||
@ -587,6 +587,13 @@ fn handle_64dd_command(connection: Connection, args: &_64DDArgs) -> Result<(), s
|
|||||||
|
|
||||||
sc64.set_save_writeback(true)?;
|
sc64.set_save_writeback(true)?;
|
||||||
|
|
||||||
|
if args.reboot && !sc64.aux_try_notify(sc64::AuxMessage::Reboot)? {
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
"Warning: no response for [Reboot] AUX message".bright_yellow()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let exit = setup_exit_flag();
|
let exit = setup_exit_flag();
|
||||||
while !exit.load(Ordering::Relaxed) {
|
while !exit.load(Ordering::Relaxed) {
|
||||||
if let Some(data_packet) = sc64.receive_data_packet()? {
|
if let Some(data_packet) = sc64.receive_data_packet()? {
|
||||||
|
@ -13,10 +13,10 @@ pub use self::{
|
|||||||
link::list_local_devices,
|
link::list_local_devices,
|
||||||
server::ServerEvent,
|
server::ServerEvent,
|
||||||
types::{
|
types::{
|
||||||
BootMode, ButtonMode, ButtonState, CicSeed, DataPacket, DdDiskState, DdDriveType, DdMode,
|
AuxMessage, BootMode, ButtonMode, ButtonState, CicSeed, DataPacket, DdDiskState,
|
||||||
DebugPacket, DiagnosticData, DiskPacket, DiskPacketKind, FpgaDebugData, ISViewer,
|
DdDriveType, DdMode, DebugPacket, DiagnosticData, DiskPacket, DiskPacketKind,
|
||||||
MemoryTestPattern, MemoryTestPatternResult, SaveType, SaveWriteback, SpeedTestDirection,
|
FpgaDebugData, ISViewer, MemoryTestPattern, MemoryTestPatternResult, SaveType,
|
||||||
Switch, TvType,
|
SaveWriteback, SpeedTestDirection, Switch, TvType,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -598,6 +598,15 @@ impl SC64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn aux_try_notify(&mut self, message: AuxMessage) -> Result<bool, Error> {
|
||||||
|
let value: u32 = message.into();
|
||||||
|
let timeout = std::time::Duration::from_millis(500);
|
||||||
|
if let Some(response) = self.aux_send_and_receive(value, timeout)? {
|
||||||
|
return Ok(value == response);
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn check_device(&mut self) -> Result<(), Error> {
|
pub fn check_device(&mut self) -> Result<(), Error> {
|
||||||
let identifier = self.command_identifier_get().map_err(|e| {
|
let identifier = self.command_identifier_get().map_err(|e| {
|
||||||
Error::new(format!("Couldn't get SC64 device identifier: {e}").as_str())
|
Error::new(format!("Couldn't get SC64 device identifier: {e}").as_str())
|
||||||
|
@ -650,6 +650,20 @@ impl TryFrom<AsynchronousPacket> for DataPacket {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum AuxMessage {
|
||||||
|
IOHalt,
|
||||||
|
Reboot,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AuxMessage> for u32 {
|
||||||
|
fn from(value: AuxMessage) -> Self {
|
||||||
|
match value {
|
||||||
|
AuxMessage::IOHalt => 0xFF000001,
|
||||||
|
AuxMessage::Reboot => 0xFF000002,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DebugPacket {
|
pub struct DebugPacket {
|
||||||
pub datatype: u8,
|
pub datatype: u8,
|
||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user