Compare commits

...

112 Commits

Author SHA1 Message Date
Robin Jones
88d654129a
[SC64][WEB] FAQ spelling fix (#109)
A minor mistake that I noticed.
2025-03-19 08:59:29 +01:00
Mateusz Faderewski
b88c9a314b [SC64][SW] Added license notice in the sc64deployer help 2025-03-12 23:05:13 +01:00
Mateusz Faderewski
e4c3f34fb0 [SC64][SW] Added warning about unknown variant of IPL3 2025-03-12 22:36:26 +01:00
Luke Stadem
b520f9ace8
[SC64][SW] Added rerun-if-changed for bootloader dependency (#108)
Changes to the bootloader source were not being noticed by cargo when
recompiling sc64deployer.

We can use
[rerun-if-changed](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed)
to instruct cargo to rerun the `build.rs` script if there were any
changes to any file inside `bootloader/src/` (recursively).
2025-03-12 18:47:50 +01:00
Mateusz Faderewski
d8c4f979cc [SC64][WEB] FAQ cleanup 2025-03-11 22:21:08 +01:00
Robin Jones
d63f5893da
[SC64][WEB] FAQ improvements (#104)
Add some improvements. take what you need.
2025-03-11 22:17:33 +01:00
Mateusz Faderewski
d307e1a5b1 [SC64][SW] Remove use keyword for std::ffi::c_char in ftdi.rs 2025-03-11 22:16:24 +01:00
liffy
65f8fa3cf7
[SC64][SW] Fix invalid variable type (description/serial) for libftdi functions in sc64deployer (#105)
`c_char` isn't `i8` on all architectures (e.g. on ARM it's `u8`)

Co-authored-by: lif <>
Co-authored-by: Mateusz Faderewski <sc@mateuszfaderewski.pl>
2025-03-11 22:14:52 +01:00
Mateusz Faderewski
6c566bd530
[SC64][CI/CD] Fix firmware building (#107) 2025-03-11 22:04:38 +01:00
Mateusz Faderewski
99060bec15 [SC64][SW] Display more meaningful error message for menu load 2025-03-11 21:20:41 +01:00
Mateusz Faderewski
63feaa0c2e [SC64][SW] Add project license information in the bootloader 2025-03-11 14:04:16 +01:00
Mateusz Faderewski
a59ad1d39b [SC64][HW] License PCB project files under CERN-OHL-S-2.0 2025-03-11 14:01:49 +01:00
Mateusz Faderewski
30fb3d0ea6 [SC64][SW] Disable auto new line in bootloader display printing functions 2025-03-09 21:14:54 +01:00
Mateusz Faderewski
a3d4082384 [SC64][SW] Added an option to force load menu when boot mode is ROM/DDIPL and R button is held 2025-03-09 20:50:24 +01:00
Mateusz Faderewski
0739ca624c [SC64][SW] Added controller reading functions in the bootloader 2025-03-09 20:48:15 +01:00
Mateusz Faderewski
bb1ce45dfe [SC64][CI/CD] Move Lattice Diamond license to GitHub secrets 2025-03-09 09:33:17 +01:00
Mateusz Faderewski
9193e9c6f2 [SC64][BUILD] Moved getting commit info to build.sh script 2025-03-08 22:14:35 +01:00
Mateusz Faderewski
3fbb6f3823 [SC64][BUILD] Moved time printing from docker_build.sh to build.sh 2025-03-08 22:12:38 +01:00
Mateusz Faderewski
f546e5d17d [SC64][SW] Implement FlashRAM timings emulation and add a fake variant of FlashRAM 2025-03-07 20:51:56 +01:00
Mateusz Faderewski
8393963650 [SC64][SW] Enable support for GPT partition scheme in FatFs + Update FatFs 2025-02-01 02:19:22 +01:00
Mateusz Faderewski
20a9ec0087 [SC64][BUILD] Force docker image platform to linux/x86_64 2025-01-27 19:50:53 +01:00
Mateusz Faderewski
b3d9e98e68 [SC64][WEB] Added a FAQ page + small updates 2025-01-27 19:49:59 +01:00
Luke Stadem
6698550dbd
[SC64][DOCS] Fix inaccurate identifier string for successful USB packets (#100)
According to my testing and the [existing sc64deployer
software](/Polprzewodnikowy/SummerCart64/blob/main/sw/deployer/src/sc64/link.rs#L186),
the identifier should be `CMP`, not `RSP`.
2024-12-24 21:01:24 +01:00
Mateusz Faderewski
18041e2547 [SC64] v2.20.2 release 2024-11-18 22:58:35 +01:00
Mateusz Faderewski
0538a28f9e [SC64][SW] Fixed regression in the SD card module introduced in the latest refactor 2024-11-18 22:57:14 +01:00
Mateusz Faderewski
1ade3ade8e [SC64] v2.20.1 release 2024-11-16 15:20:12 +01:00
Mateusz Faderewski
80b4aa95cd [SC64] Updated GitHub issue template 2024-11-16 13:59:46 +01:00
Mateusz Faderewski
6eef811cd6 [SC64][SW] Fix SD deinit error when the card is not locked + SD module refactor 2024-11-16 13:47:20 +01:00
Raphaël Tétreault
e2c100ae7f
[SC64] Revise README to include Feature subheadings for easy at-a-glance reading (#77)
I made some minor changes to the README to try and address the most
common read-the-manual moments seen in the N64Brew Discord.

Specifically, I broke out the features listed in the README into
sub-categories, with the non-developer features up at the top for
non-technical people to read first.

The categories are:
- Hardware-Dependent Game Features
- Game Saves
- Menu
- Game Development
- Cartridge Production

Co-authored-by: Mateusz Faderewski <sc@mateuszfaderewski.pl>
2024-10-11 19:09:41 +02:00
Mateusz Faderewski
a6e86587ae [SC64][DOCS][WEB] Updated readme, documentation and website 2024-10-11 18:36:10 +02:00
Mateusz Faderewski
93ab101be4 [SC64] Updated GitHub issue/PR templates 2024-10-11 18:26:46 +02:00
Mateusz Faderewski
cc41652e6f [SC64][DOCS][WEB] Added a link to a video build guide 2024-10-09 11:19:25 +02:00
Mateusz Faderewski
ed63eb3e8c [SC64] v2.20.0 release 2024-09-29 21:14:55 +02:00
Mateusz Faderewski
db4f16754f [SC64][SW] Release SD lock on init error 2024-09-29 21:14:32 +02:00
Mateusz Faderewski
74e20cb8cc
[SC64][SW] Added access to the SD card via USB interface (#90) 2024-09-29 20:58:52 +02:00
Mateusz Faderewski
3146cc8c99 [SC64][SW] Fix USB reset procedure (I/O buffer purge) 2024-09-03 12:48:36 +02:00
Mateusz Faderewski
4a50e33acd [SC64][WEB] Updated info on the website 2024-08-24 22:02:25 +02:00
Mateusz Faderewski
d12bfaabf6 [SC64][SW] Fix timeout issues on slow backends in sc64deployer 2024-08-18 13:30:24 +02:00
Mateusz Faderewski
04cecb1955 [SC64] v2.19.0 release 2024-08-17 18:23:57 +02:00
Mateusz Faderewski
a5284aa770 [SC64][SW] Updated sc64env docker image 2024-08-17 16:53:57 +02:00
Giovanni Bajo
bbcf041b5a
[SC64][SW] Allow lowercase confirmation (#85)
At least one user fell for this, so we can allow lowercase to reduce
support issues
2024-08-17 15:06:41 +02:00
Mateusz Faderewski
12e16b807a [SC64][DOCS][WEB] Updated website and documentation 2024-08-16 13:59:46 +02:00
Mateusz Faderewski
92fb4a85df
[SC64][FW][SW] New framework for SC64 IRQ handling (#68) 2024-08-16 13:52:01 +02:00
Mateusz Faderewski
1259687902 [SC64][HW] Changed battery holder from 1060 to 1058 + minor PCB improvements 2024-08-11 15:41:09 +02:00
Mateusz Faderewski
d8976def97 [SC64][SW] Optimized memory usage in the sc64deployer 2024-07-23 18:26:17 +02:00
Mateusz Faderewski
e9ee025e21 [SC64][FW][SW] Slightly speed up DMA write transfer speed + USB speed test fixes 2024-07-21 22:15:06 +02:00
Mateusz Faderewski
912f356650 [SC64][SW] Added USB speed test 2024-07-21 20:15:44 +02:00
Mateusz Faderewski
631f140c48 [SC64][SW] Adjust buffer sizes in sc64deployer to tune for maximum transfer speed 2024-07-21 12:19:03 +02:00
Mateusz Faderewski
71cef2cd6c [SC64][SW] Change error type starting ID to avoid reusing old error code IDs 2024-07-21 12:17:44 +02:00
Mateusz Faderewski
1b71b4a333
[SC64][FW] Verilator tests + many bugfixes in the FPGA code (#75) 2024-07-21 11:15:08 +02:00
Mateusz Faderewski
5e33e516a2
[SC64][SW] Implement new libftdi backend in the sc64deployer (#72) 2024-07-21 10:48:20 +02:00
Mena
5adc95b6e1
[SC64][HW] Update design of sc64_shell_button_b3fs-105x_with_end_stop.step (#74)
button redesign to stop rattle
2024-07-07 19:16:40 +02:00
Mateusz Faderewski
acc3e588d8 [SC64][SW] Fixed RTC raw time offset calculation 2024-06-15 00:33:43 +02:00
Mateusz Faderewski
a571fe16f5 [SC64][SW] Added support for 400 leap years for the RTC 2024-06-14 21:47:10 +02:00
Mateusz Faderewski
903efe5353 [SC64][SW] Add missing cleanup during SD card initialization 2024-06-14 21:46:02 +02:00
Mateusz Faderewski
e4af127e55 [SC64][FW][SW] Added option to set and read century when updating RTC datetime 2024-06-06 23:00:38 +02:00
Mateusz Faderewski
6bbfee44e7 [SC64][SW] Added inverted own address SDRAM tests 2024-05-20 16:23:41 +02:00
Mateusz Faderewski
9843a79a86 [SC64][SW] controller: fixed stuck LED state when error was cleared 2024-05-20 16:18:14 +02:00
Mateusz Faderewski
80c06f3e53 [SC64][DOCS] Added FTDI driver installation requirement in the build guide 2024-05-16 21:07:38 +02:00
Mateusz Faderewski
554305290e [SC64][SW] controller: corrected SD card init procedure 2024-05-14 19:39:00 +02:00
Mateusz Faderewski
e6751c262d [SC64][SW] bootloader/controller: added more meaningful errors in the SD card module
Also added manual CRC16 check in the software for data blocks smaller than 512 bytes
2024-05-14 01:36:34 +02:00
Mateusz Faderewski
92838da349 [SC64][SW] Added SD card FatFs tests, rearranged SDRAM tests 2024-05-12 02:45:56 +02:00
Mateusz Faderewski
a884d69308 [SC64][SW] sc64deployer: use chunked write/read for memory access 2024-05-08 21:31:29 +02:00
Mateusz Faderewski
7bc4e6d180 [SC64][SW] sc64deployer: added SDRAM tests 2024-05-08 20:24:08 +02:00
Mateusz Faderewski
a0bd0ddd98 [SC64][SW] Bootloader: rearranged tests and added/changed SDRAM patterns 2024-05-07 00:42:07 +02:00
Mateusz Faderewski
b8632a305b [SC64][HW] Added back notes column to the interactive BOM file 2024-05-06 00:15:08 +02:00
Mateusz Faderewski
b89ca68cb4 [SC64][SW] Bootloader: added PI tests and improved SDRAM test reliability 2024-05-05 21:01:46 +02:00
Mateusz Faderewski
0f3eaa6d17 [SC64][DOCS] Updated readme 2024-05-05 16:15:26 +02:00
Mateusz Faderewski
5854b4ec0d [SC64][WEB] Add BOM directly on the website 2024-05-05 16:03:06 +02:00
Mateusz Faderewski
9599db8307 [SC64][FW] Set timing constraints for SDRAM pins 2024-05-05 07:08:44 +02:00
Mateusz Faderewski
cf19dc6151 [SC64][SW] Update: allow firmware update from the Flash memory 2024-05-05 01:53:19 +02:00
Mateusz Faderewski
0e868b5ad5 [SC64][FW] Added placeholder test point outputs in the FPGA project 2024-05-03 17:55:40 +02:00
Mateusz Faderewski
8a49e954ce [SC64][BUILD] Fixed BOM source path in builder script 2024-05-03 17:53:34 +02:00
Mateusz Faderewski
3b13d21e63 [SC64][FW] CIC: use output enable register in PIO 2024-05-03 17:51:20 +02:00
Mateusz Faderewski
4eaa0b3353 [SC64][FW] Flash: fixed partial page write + handle data mask during write
Flash memory module had an issue when ending address was not page (256 bytes) aligned.

Now it's possible to write single bytes to the Flash instead of being forced to do 16 bit aligned writes.
2024-05-03 17:49:44 +02:00
Mateusz Faderewski
64c3b69454 [SC64][DOCS][WEB] Updated readme and website 2024-04-30 19:20:02 +02:00
Mateusz Faderewski
83bcfd3065 [SC64][HW] Updated license and author info on the PCB 2024-04-17 17:37:52 +02:00
Mateusz Faderewski
61bd3c62d8 [SC64][HW] Updated PCB date and revision 2024-04-17 17:04:36 +02:00
Mateusz Faderewski
2846938c5f [SC64][HW] Added README.md for shell design files 2024-04-17 16:51:42 +02:00
Mateusz Faderewski
0050688255 [SC64][HW] Organize and rename shell design files 2024-04-17 16:47:56 +02:00
Mateusz Faderewski
03daabeae8
[SC64][HW] Add shell models for injection molding and expand screw hole size in the PCB (#69)
Co-authored-by: Mena <emdazer@gmail.com>
2024-04-17 16:34:10 +02:00
Mateusz Faderewski
e93cecfae3 [SC64][FW][SW] Fixed unreliable CIC boot 2024-04-11 01:34:36 +02:00
Mateusz Faderewski
0150060f1e [SC64][DOCS] Updated readme 2024-04-10 21:48:00 +02:00
Mateusz Faderewski
42b10e85f1 [SC64][DOCS] Updated readme 2024-04-10 21:45:42 +02:00
Mateusz Faderewski
cff730cafc [SC64][SW] Minor formatting and spelling fixes 2024-04-10 21:32:17 +02:00
Mateusz Faderewski
f6b94aec97 [SC64][FW][SW] Added CIC diagnostic, changed software timeout timer to hardware (derived from PIF clock) 2024-04-10 12:56:05 +02:00
Mateusz Faderewski
c156b72bee [SC64][SW] Added very basic SDRAM test in the primer before flashing bootloader 2024-03-17 02:04:05 +01:00
Mateusz Faderewski
f27f644a7e [SC64][SW] Fixed SC64 reset procedure in the deployer 2024-03-14 20:07:07 +01:00
Mateusz Faderewski
5b880fc052 [SC64][BUILD] Replaced website publish action 2024-03-04 15:29:10 +01:00
Mateusz Faderewski
a12641bf39 [SC64][BUILD] Bumped GitHub Actions dependencies 2024-03-04 12:55:47 +01:00
Mateusz Faderewski
01bef4060e [SC64][SW] Updated deployer dependencies 2024-03-04 12:47:24 +01:00
Mateusz Faderewski
b4b3659458 [SC64][SW] Fix buffered receive bug in sc64deployer server 2024-03-04 00:54:51 +01:00
Mateusz Faderewski
7a83fb3eae [SC64][FW] Updated Lattice Diamond license.dat file 2024-03-01 22:15:30 +01:00
Mateusz Faderewski
66f71c2040 [SC64][WEB] Updated website content (64DD section) 2024-01-29 14:30:49 +01:00
Mateusz Faderewski
95b78f3cd9 [SC64] v2.18.1 release 2024-01-29 14:26:22 +01:00
Mateusz Faderewski
421d0438f3
[SC64][FW][SW] Controller rewrite to remove task subsystem + minor bug fixes (#64)
This PR completely removes `task.c / task.h` from `sw/controller` STM32
code.
Additionally, these changes were implemented:
- Updated IPL3
- Added new diagnostic data (voltage and temperature) readout commands
for USB and N64
- Fixed some issues with FlashRAM save type
- Joybus timings were relaxed to accommodate communication with
unsynchronized master controller (like _Datel Game Killer_, thanks
@RWeick)
- N64 embedded test program now waits for release of button press to
proceed
- Fixed issue where, in rare circumstances, I2C peripheral in STM32
would get locked-up on power-up
- LED blinking behavior on SD card access was changed
- LED blink duration on save writeback has been extended
- Minor fixes through the entire of hardware abstraction layer for STM32
code
- Primer now correctly detects issues with I2C bus during first time
programming
- `primer.py` script gives more meaningful error messages
- Fixed bug where RTC time was always written on N64FlashcartMenu boot
- sc64deployer now displays "Diagnostic data" instead of "MCU stack
usage"
2024-01-29 14:23:18 +01:00
Mateusz Faderewski
be37025d42 [SC64][WEB] Added option to buy SC64 in Phenom Mod store 2024-01-25 08:33:33 +01:00
Mateusz Faderewski
17c12f5957 [SC64][WEB] Updated website content + made menu buttons bigger 2024-01-21 20:37:07 +01:00
Mateusz Faderewski
f7eb6a73b4 [SC64][SW] Removed _oscillator running after time set_ check in the RTC, lowered I2C clock speed 2024-01-12 03:12:56 +01:00
Mateusz Faderewski
b84213b3e4 [SC64][WEB] Fix embed image 2024-01-11 09:18:28 +01:00
Mateusz Faderewski
356f1f2f0f [SC64][WEB] Added embed meta info 2024-01-11 09:06:13 +01:00
Mateusz Faderewski
42acb48004 [SC64][WEB] Added rendered PCB images 2024-01-11 05:23:03 +01:00
Mateusz Faderewski
1f7be611d8 [SC64][WEB] Updated website home page 2024-01-11 02:22:52 +01:00
Mateusz Faderewski
13fc38fe73 [SC64][SW] Implement correct behavior for FlashRAM writes 2024-01-10 22:38:04 +01:00
Mateusz Faderewski
2639a93b6f [SC64][WEB] Fix menu CSS 2024-01-10 22:35:30 +01:00
Mateusz Faderewski
7ea74f19a5 [SC64][WEB] Added favicon 2024-01-07 20:04:24 +01:00
Mateusz Faderewski
7f21902e21 [SC64][WEB] Fix publishing website 2024-01-07 19:25:47 +01:00
Mateusz Faderewski
d7aadf48f9 [SC64][WEB] Added initial website 2024-01-07 19:21:11 +01:00
Mason Stooksbury
5ae3e32d63
[SC64][HW] Added small piece to reach the tactile button from the back shell (#61)
Adding a small piece to reach the button from the back shell. Fits
great, works well, and doesn't fall out

![button](https://github.com/Polprzewodnikowy/SummerCart64/assets/40926080/f35e3420-af41-4476-81e3-f46825c84245)

![IMG20231221114136](https://github.com/Polprzewodnikowy/SummerCart64/assets/40926080/66868832-cc00-492c-9d95-b3c9fb0c7944)

![IMG20231221114114](https://github.com/Polprzewodnikowy/SummerCart64/assets/40926080/c5c867ce-c1ea-4933-b84f-29746245c92d)
2023-12-21 23:59:08 +01:00
Mason Stooksbury
ef053f9c52
[SC64][DOCS] Update 06_build_guide.md regarding shell screws (#62)
Updated the Build Guide with details around which screws work best for
putting the shell together
2023-12-21 21:57:07 +01:00
Mateusz Faderewski
3ed1ad4d73
[SC64][SW] Add Windows 32-bit build for sc64deployer (#60) 2023-12-17 21:11:20 +01:00
Mateusz Faderewski
c14da52f46
[SC64] Fix linguist statistics 2023-12-15 00:44:32 +01:00
180 changed files with 190643 additions and 73911 deletions

2
.gitattributes vendored
View File

@ -1,5 +1,5 @@
fw/project/lcmxo2/*.sty linguist-generated fw/project/lcmxo2/*.sty linguist-generated
fw/rtl/serv/* -linguist-vendored fw/rtl/serv/* linguist-vendored
fw/rtl/vendor/** -linguist-vendored fw/rtl/vendor/** -linguist-vendored
fw/rtl/vendor/lcmxo2/generated/* linguist-generated fw/rtl/vendor/lcmxo2/generated/* linguist-generated
hw/pcb/*.html linguist-generated hw/pcb/*.html linguist-generated

1
.github/FUNDING.yml vendored
View File

@ -1,2 +1 @@
github: polprzewodnikowy
ko_fi: polprzewodnikowy ko_fi: polprzewodnikowy

View File

@ -1,27 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1.
2.
3.
4.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Possible solution**
Not obligatory, but suggest a fix/reason for the bug.

58
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@ -0,0 +1,58 @@
name: Bug Report
description: File a bug report
title: "[SC64][BUG] "
labels: ["bug"]
assignees:
- Polprzewodnikowy
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: checkboxes
id: sanity-check
attributes:
label: Is your issue really a bug?
description: |
Issue tracker in this repository is for **BUG REPORTS ONLY**.
Make sure your problem is caused by the firmware/PC app and **not** by the software you're running on the flashcart.
Errors in the documentation are also considered a bug.
If your issue is related to the menu then report it in the [N64FlashcartMenu] repository.
[N64FlashcartMenu]: https://github.com/Polprzewodnikowy/N64FlashcartMenu
options:
- label: I understand the difference between flashcart firmware, N64FlashcartMenu and `sc64deployer` PC app.
required: true
- label: I found a bug in FPGA HDL (`/fw/rtl`)
- label: I found a bug in MCU app (`/sw/controller`)
- label: I found a bug in N64 bootloader (`/sw/bootloader`)
- label: I found a bug in PC app (`/sw/deployer`)
- label: I found a bug in initial programming script (`/sw/tools/primer.py`)
- label: I found an error in documentation (`/docs`)
- label: I found an issue elsewhere
- type: input
id: version
attributes:
label: Firmware version
placeholder: v2.20.2
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: Describe the bug
description: |
Tell us what you noticed as a bug, and what is your expected outcome.
The more detailed the description is the better.
If applicable please attach screenshots and/or video showing the problem.
validations:
required: true
- type: textarea
id: deployer-info
attributes:
label: Output logs from `sc64deployer info`
description: If possible, please copy and paste the output from the command specified above.
render: shell

View File

@ -1,8 +1,5 @@
blank_issues_enabled: false blank_issues_enabled: false
contact_links: contact_links:
- name: GitHub Discussions
url: https://github.com/Polprzewodnikowy/SummerCart64/discussions
about: Please use for QUESTIONS, conversations or discussions.
- name: N64brew Discord - name: N64brew Discord
url: https://discord.gg/8xvRPqCAFC url: https://discord.gg/8VNMKhxqQn
about: Alternative channel for asking QUESTIONS. about: The go-to community to ask about SummerCart64

View File

@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -1,20 +0,0 @@
**Description**
Provide a headline summary of your changes in the Title above.
Describe your changes in detail.
**Related Issue**
This project only accepts pull requests related to open issues.
If suggesting a change, please discuss it in an issue first.
If fixing a bug, there should be an issue describing it with steps to reproduce.
Please link to the issue here:
**Motivation and Context**
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.
**How Has This Been Tested?**
Please describe in detail how you tested your changes.
Include details of your testing environment, and the tests you ran to see how your change affects other areas of the code, etc.
**Screenshots**
If applicable, add screenshots to help demonstrate your feature/bugfix.

View File

@ -2,7 +2,7 @@ name: build
on: on:
push: push:
branches: branches:
- main - main
pull_request: pull_request:
branches: branches:
@ -18,7 +18,9 @@ jobs:
steps: steps:
- name: Download SummerCart64 repository - name: Download SummerCart64 repository
uses: actions/checkout@v3 uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set package version - name: Set package version
uses: frabert/replace-string-action@v2 uses: frabert/replace-string-action@v2
@ -28,13 +30,19 @@ jobs:
string: '${{ github.ref_name }}' string: '${{ github.ref_name }}'
replace-with: '-' replace-with: '-'
- name: Retrieve the Lattice Diamond license from secrets and decode it
run: echo $LICENSE | base64 --decode > fw/project/lcmxo2/license.dat
env:
LICENSE: ${{ secrets.LATTICE_DIAMOND_LICENSE_BASE64 }}
- name: Build firmware - name: Build firmware
run: ./docker_build.sh release --force-clean run: ./docker_build.sh release --force-clean
env: env:
MAC_ADDRESS: ${{ secrets.LATTICE_DIAMOND_MAC_ADDRESS }}
SC64_VERSION: ${{ steps.version.outputs.replaced }} SC64_VERSION: ${{ steps.version.outputs.replaced }}
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: sc64-pkg-${{ steps.version.outputs.replaced }} name: sc64-pkg-${{ steps.version.outputs.replaced }}
path: | path: |
@ -52,32 +60,43 @@ jobs:
build-deployer: build-deployer:
strategy: strategy:
matrix: matrix:
os: [windows-latest, ubuntu-latest, macos-latest] version: [windows, windows-32bit, linux, macos]
include: include:
- os: windows-latest - version: windows
executable: sc64deployer.exe os: windows-latest
name: sc64-deployer-windows executable: target/release/sc64deployer.exe
options: -c -a -f package-name: sc64-deployer-windows
extension: zip package-params: -c -a -f
package-extension: zip
- os: ubuntu-latest - version: windows-32bit
linux-packages: libudev-dev os: windows-latest
executable: sc64deployer build-params: --target=i686-pc-windows-msvc
name: sc64-deployer-linux executable: target/i686-pc-windows-msvc/release/sc64deployer.exe
options: -czf package-name: sc64-deployer-windows-32bit
extension: tar.gz package-params: -c -a -f
package-extension: zip
- os: macos-latest - version: linux
executable: sc64deployer os: ubuntu-latest
name: sc64-deployer-macos apt-packages: libudev-dev
options: -czf executable: target/release/sc64deployer
extension: tgz package-name: sc64-deployer-linux
package-params: -czf
package-extension: tar.gz
- version: macos
os: macos-latest
executable: target/release/sc64deployer
package-name: sc64-deployer-macos
package-params: -czf
package-extension: tgz
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Download SummerCart64 repository - name: Download SummerCart64 repository
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Set package version - name: Set package version
uses: frabert/replace-string-action@v2 uses: frabert/replace-string-action@v2
@ -87,32 +106,66 @@ jobs:
string: '${{ github.ref_name }}' string: '${{ github.ref_name }}'
replace-with: '-' replace-with: '-'
- name: Install linux packages - name: Install apt packages
if: matrix.linux-packages if: matrix.apt-packages
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get -y install ${{ matrix.linux-packages }} sudo apt-get -y install ${{ matrix.apt-packages }}
- name: Build deployer - name: Build deployer
run: cargo b -r run: cargo b -r ${{ matrix.build-params }}
working-directory: sw/deployer working-directory: sw/deployer
- name: Package executable - name: Package executable
run: | run: |
mkdir package mkdir -p ./package
cd target/release cp ${{ matrix.executable }} ./package/
tar ${{ matrix.options }} ../../package/${{ matrix.name }}-${{ steps.version.outputs.replaced }}.${{ matrix.extension }} ${{ matrix.executable }} cd ./package
tar ${{ matrix.package-params }} ./${{ matrix.package-name }}-${{ steps.version.outputs.replaced }}.${{ matrix.package-extension }} *
working-directory: sw/deployer working-directory: sw/deployer
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: ${{ matrix.name }}-${{ steps.version.outputs.replaced }} name: ${{ matrix.package-name }}-${{ steps.version.outputs.replaced }}
path: sw/deployer/package/${{ matrix.name }}-${{ steps.version.outputs.replaced }}.${{ matrix.extension }} path: sw/deployer/package/${{ matrix.package-name }}-${{ steps.version.outputs.replaced }}.${{ matrix.package-extension }}
- name: Upload release assets - name: Upload release assets
if: github.event_name == 'release' && github.event.action == 'created' if: github.event_name == 'release' && github.event.action == 'created'
uses: softprops/action-gh-release@v0.1.15 uses: softprops/action-gh-release@v0.1.15
with: with:
files: | files: |
sw/deployer/package/${{ matrix.name }}-${{ steps.version.outputs.replaced }}.${{ matrix.extension }} sw/deployer/package/${{ matrix.package-name }}-${{ steps.version.outputs.replaced }}.${{ matrix.package-extension }}
publish-website:
if: github.ref == 'refs/heads/main'
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Download SummerCart64 repository
uses: actions/checkout@v4
- name: Copy BOM file to the website folder
run: cp ./hw/pcb/sc64v2_bom.html ./web
- name: Setup GitHub pages
uses: actions/configure-pages@v4
- name: Upload website artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./web
- name: Publish website from uploaded artifact
id: deployment
uses: actions/deploy-pages@v4

View File

@ -1,25 +1,43 @@
# SummerCart64 - a fully open source Nintendo 64 flashcart # SummerCart64 - a fully open source N64 flashcart
[<img src="assets/sc64_logo.svg" />](assets/sc64_logo.svg) [<img src="assets/sc64_logo.svg" />](assets/sc64_logo.svg)
**For non-technical description of the SummerCart64, please head to the https://summercart64.dev website!**
## Features ## Features
- 64 MiB SDRAM memory for game and save data
- 16 MiB FLASH memory for bootloader and extended game data - **ROM and Save Memory On-board**
- 8 kiB on-chip buffer for general use - 64 MiB SDRAM memory for game and save data (enough memory to support every retail game without compromise)
- ~23.8 MiB/s peak transfer rate USB interface for data upload/download and debug functionality - 16 MiB FLASH memory for bootloader and extended game data (with extended memory flashcart supports game ROMs up to 78 MiB)
- ~23.8 MiB/s peak transfer rate SD card interface
- EEPROM, SRAM and FlashRAM save types with automatic writeback to SD card - **Game Saves**
- Battery backed real time clock (RTC) - EEPROM 4k/16k, SRAM and FlashRAM save types with an automatic writeback to the SD card (no reset button press required)
- Status LED and button for general use
- 64DD add-on emulation - **Hardware-Dependent Game Features**
- IS-Viewer 64 debug interface - 64DD add-on emulation
- N64 bootloader with support for IPL3 registers spoofing and loading menu from SD card - Battery backed real time clock (RTC)
- Dedicated open source menu written specifically for this flashcart - [N64FlashcartMenu](https://github.com/Polprzewodnikowy/N64FlashcartMenu)
- Enhanced [UltraCIC_C](https://github.com/jago85/UltraCIC_C) emulation with automatic region switching and programmable seed/checksum values - **Menu**
- PC app for communicating with flashcart (game/save data upload/download, feature enable control and debug terminal) - Dedicated open source menu written specifically for this flashcart - [N64FlashcartMenu](https://github.com/Polprzewodnikowy/N64FlashcartMenu)
- [UNFLoader](https://github.com/buu342/N64-UNFLoader) support
- Initial programming via UART header or dedicated JTAG/SWD interfaces - **Game Development**
- Software and firmware updatable via USB interface - ~23.8 MiB/s peak transfer rate SD card interface
- 3D printable plastic shell - ~23.8 MiB/s peak transfer rate USB interface for data upload/download and debug functionality
- PC app to access the flashcart features:
- Game/save data upload/download
- Feature enable control
- Debug terminal
- Access to the SD card
- Firmware update
- [UNFLoader](https://github.com/buu342/N64-UNFLoader) support
- IS-Viewer 64 debug interface (fixed 64 kiB buffer with a movable base address)
- 8 kiB on-chip buffer for general use
- Status LED and button for general use
- [UltraCIC_C](https://github.com/jago85/UltraCIC_C) emulation with automatic region switching and programmable seed/checksum values
- N64 bootloader with support for IPL3 registers spoofing and loading menu from SD card
- **Cartridge Production**
- Initial programming via UART header or via dedicated JTAG/SWD interfaces
- 3D printable shell
--- ---
@ -35,30 +53,22 @@
--- ---
## Help / Q&A
For any questions related to this project, please use [*Discussions*](https://github.com/Polprzewodnikowy/SummerCart64/discussions) tab in GitHub repository.
Using discussions tab is highly encouraged as it allows to have centralized knowledge database accessible for everyone interested in this project.
I'm also active at [N64brew](https://discord.gg/WqFgNWf) Discord server as `korgeaux` but keep in mind that [*Discussions*](https://github.com/Polprzewodnikowy/SummerCart64/discussions) tab is a preferred option.
---
## How do I get one? ## How do I get one?
One option is to ask in `#summer-cart-64` channel on [N64brew](https://discord.gg/WqFgNWf) Discord server if someone is making a group order. Most up to date information about purchasing/manufacturing options is available on https://summercart64.dev website!
If you want to order it yourself then I've prepared all necessary manufacturing files on the [PCBWay Shared Project](https://www.pcbway.com/project/shareproject/SC64_an_open_source_Nintendo_64_flashcart_14b9688a.html) site. If you want to order it yourself then I've prepared all necessary manufacturing files on the [PCBWay Shared Project](https://www.pcbway.com/project/member/shareproject/?bmbno=1046ED64-8AEE-44) site.
**Full disclosure**: for every order made through [this link](https://www.pcbway.com/project/shareproject/SC64_an_open_source_Nintendo_64_flashcart_14b9688a.html) I will receive 10% of PCB manufacturing and PCB assembly service cost (price of the components is not included in the split). This is a great way of supporting further project development.
**Be careful**: this is an advanced project and it is assumed that you have enough knowledge about electronics. **Be careful**: this is an advanced project and it is assumed that you have enough knowledge about electronics.
Selecting wrong options or giving PCB manufacturer wrong information might result in an undesired time and/or money loss. Selecting wrong options or giving PCB manufacturer wrong information might result in an undesired time and/or money loss.
Boards also come unprogrammed from the manufacturer - you need to do **initial programming step** yourself after receiving the board. Boards also come unprogrammed from the manufacturer - you need to do **initial programming step** yourself after receiving the board.
**Price of the components is not included in the initial quote at the checkout** - manufacturer will contact you later with updated price.
To avoid problems _**please**_ read **both** [build guide](./docs/06_build_guide.md) and description on the shared project page **in full**. To avoid problems _**please**_ read **both** [build guide](./docs/06_build_guide.md) and description on the shared project page **in full**.
If you have even slightest doubt about the ordering or programming process, it is better to leave it to someone experienced - ask in the Discord server mentioned above if that's the case. If you have even slightest doubt about the ordering or programming process, it is better to leave it to someone experienced - ask in the [N64brew Discord server](https://discord.gg/8VNMKhxqQn) if that's the case.
If you don't need a physical product but still want to support me then check my [GitHub sponsors](https://github.com/sponsors/Polprzewodnikowy) page. **Full disclosure**: for every order made through [this link](https://www.pcbway.com/project/member/shareproject/?bmbno=1046ED64-8AEE-44) I will receive 10% of PCB manufacturing and PCB assembly service cost (price of the components is not included in the split). This is a great way of supporting further project development.
If you don't need a physical product but still want to support me then check the sponsor links on the [official website](https://summercart64.dev).
--- ---
@ -82,8 +92,10 @@ If you don't need a physical product but still want to support me then check my
This project wouldn't be possible without these contributions: This project wouldn't be possible without these contributions:
- [64drive](https://64drive.retroactive.be) orders being on permanent hold long before creating this repository. - [64drive](https://64drive.retroactive.be) ([archived](https://web.archive.org/web/20240406215731/https://64drive.retroactive.be/)) orders being on permanent hold long before creating this repository.
- [EverDrive-64 X7](https://krikzz.com/our-products/cartridges/ed64x7.html) being disappointment for homebrew development (slow USB upload, unjustified price and overcomplicated SD card access). - [EverDrive-64 X7](https://krikzz.com/our-products/cartridges/ed64x7.html) being disappointment for homebrew development (slow USB upload, unjustified price and overcomplicated SD card access).
- Context: Both aforementioned products were priced at $199 in 2020. 64drive features made it a vastly more useful tool for homebrew development.
Since then, 64drive had never been restocked and EverDrive-64 X7 price was lowered to $159 (as of May 2024).
- [Jan Goldacker (@jago85)](https://github.com/jago85) and his projects: - [Jan Goldacker (@jago85)](https://github.com/jago85) and his projects:
- [Brutzelkarte](https://github.com/jago85/Brutzelkarte_FPGA) providing solid base for starting this project and sparking hope for true open source N64 flashcarts. - [Brutzelkarte](https://github.com/jago85/Brutzelkarte_FPGA) providing solid base for starting this project and sparking hope for true open source N64 flashcarts.
- [UltraCIC_C](https://github.com/jago85/UltraCIC_C) reimplementation for easy integration in modern microcontrollers. Thanks also goes to everyone involved in N64 CIC reverse engineering. - [UltraCIC_C](https://github.com/jago85/UltraCIC_C) reimplementation for easy integration in modern microcontrollers. Thanks also goes to everyone involved in N64 CIC reverse engineering.

View File

@ -17,7 +17,8 @@ TOP_FILES=(
FILES=( FILES=(
"./assets/*" "./assets/*"
"./docs/*" "./docs/*"
"./hw/pcb/sc64_hw_v2.0a_bom.html" "./hw/pcb/LICENSE"
"./hw/pcb/sc64v2_bom.html"
"./hw/pcb/sc64v2.kicad_pcb" "./hw/pcb/sc64v2.kicad_pcb"
"./hw/pcb/sc64v2.kicad_pro" "./hw/pcb/sc64v2.kicad_pro"
"./hw/pcb/sc64v2.kicad_sch" "./hw/pcb/sc64v2.kicad_sch"
@ -27,6 +28,8 @@ FILES=(
"./README.md" "./README.md"
) )
HAVE_COMMIT_INFO=false
BUILT_BOOTLOADER=false BUILT_BOOTLOADER=false
BUILT_CONTROLLER=false BUILT_CONTROLLER=false
BUILT_CIC=false BUILT_CIC=false
@ -36,9 +39,24 @@ BUILT_RELEASE=false
FORCE_CLEAN=false FORCE_CLEAN=false
get_last_commit_info () {
if [ "$HAVE_COMMIT_INFO" = true ]; then return; fi
SAFE_DIRECTORY="-c safe.directory=$(pwd)"
GIT_BRANCH=$(git $SAFE_DIRECTORY rev-parse --abbrev-ref HEAD)
GIT_TAG=$(git $SAFE_DIRECTORY describe --tags 2> /dev/null)
GIT_SHA=$(git $SAFE_DIRECTORY rev-parse HEAD)
GIT_MESSAGE=$(git $SAFE_DIRECTORY log --oneline --format=%B -n 1 HEAD | head -n 1)
HAVE_COMMIT_INFO=true
}
build_bootloader () { build_bootloader () {
if [ "$BUILT_BOOTLOADER" = true ]; then return; fi if [ "$BUILT_BOOTLOADER" = true ]; then return; fi
get_last_commit_info
pushd sw/bootloader > /dev/null pushd sw/bootloader > /dev/null
if [ "$FORCE_CLEAN" = true ]; then if [ "$FORCE_CLEAN" = true ]; then
make clean make clean
@ -83,6 +101,8 @@ build_cic () {
build_fpga () { build_fpga () {
if [ "$BUILT_FPGA" = true ]; then return; fi if [ "$BUILT_FPGA" = true ]; then return; fi
build_cic
pushd fw/project/lcmxo2 > /dev/null pushd fw/project/lcmxo2 > /dev/null
if [ "$FORCE_CLEAN" = true ]; then if [ "$FORCE_CLEAN" = true ]; then
rm -rf ./impl1/ rm -rf ./impl1/
@ -96,6 +116,8 @@ build_fpga () {
build_update () { build_update () {
if [ "$BUILT_UPDATE" = true ]; then return; fi if [ "$BUILT_UPDATE" = true ]; then return; fi
get_last_commit_info
build_bootloader build_bootloader
build_controller build_controller
build_cic build_cic
@ -161,6 +183,14 @@ if test $# -eq 0; then
exit 1 exit 1
fi fi
print_time () {
echo "Build took $SECONDS seconds"
}
trap "echo \"Build failed\"; print_time" ERR
SECONDS=0
TRIGGER_BOOTLOADER=false TRIGGER_BOOTLOADER=false
TRIGGER_CONTROLLER=false TRIGGER_CONTROLLER=false
TRIGGER_CIC=false TRIGGER_CIC=false
@ -211,3 +241,5 @@ if [ "$TRIGGER_CIC" = true ]; then build_cic; fi
if [ "$TRIGGER_FPGA" = true ]; then build_fpga; fi if [ "$TRIGGER_FPGA" = true ]; then build_fpga; fi
if [ "$TRIGGER_UPDATE" = true ]; then build_update; fi if [ "$TRIGGER_UPDATE" = true ]; then build_update; fi
if [ "$TRIGGER_RELEASE" = true ]; then build_release; fi if [ "$TRIGGER_RELEASE" = true ]; then build_release; fi
print_time

View File

@ -1,20 +1,14 @@
#!/bin/bash #!/bin/bash
BUILDER_IMAGE="ghcr.io/polprzewodnikowy/sc64env:v1.8" BUILDER_IMAGE="ghcr.io/polprzewodnikowy/sc64env:v1.10"
BUILDER_PLATFORM="linux/x86_64"
pushd $(dirname $0) > /dev/null pushd $(dirname $0) > /dev/null
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
GIT_TAG=$(git describe --tags 2> /dev/null)
GIT_SHA=$(git rev-parse HEAD)
GIT_MESSAGE=$(git log --oneline --format=%B -n 1 HEAD | head -n 1)
if [ -t 1 ]; then if [ -t 1 ]; then
DOCKER_OPTIONS="-it" DOCKER_OPTIONS="-it"
fi fi
SECONDS=0
docker run \ docker run \
$DOCKER_OPTIONS \ $DOCKER_OPTIONS \
--rm \ --rm \
@ -23,20 +17,13 @@ docker run \
-v "$(pwd)"/fw/project/lcmxo2/license.dat:/flexlm/license.dat \ -v "$(pwd)"/fw/project/lcmxo2/license.dat:/flexlm/license.dat \
-v "$(pwd)":/workdir \ -v "$(pwd)":/workdir \
-h=`hostname` \ -h=`hostname` \
-e GIT_BRANCH="$GIT_BRANCH" \
-e GIT_TAG="$GIT_TAG" \
-e GIT_SHA="$GIT_SHA" \
-e GIT_MESSAGE="$GIT_MESSAGE" \
-e SC64_VERSION=${SC64_VERSION:-""} \ -e SC64_VERSION=${SC64_VERSION:-""} \
--platform $BUILDER_PLATFORM \
$BUILDER_IMAGE \ $BUILDER_IMAGE \
./build.sh $@ ./build.sh $@
BUILD_ERROR=$? BUILD_RESULT=$?
echo "Build took $SECONDS seconds"
popd > /dev/null popd > /dev/null
if [ $BUILD_ERROR -ne 0 ]; then exit $BUILD_RESULT
exit -1
fi

View File

@ -1,50 +1,38 @@
- [First time setup on PC](#first-time-setup-on-pc) - [First time setup](#first-time-setup)
- [Firmware backup/update](#firmware-backupupdate) - [Standalone mode (Running menu and games on the N64)](#standalone-mode-running-menu-and-games-on-the-n64)
- [Running menu in standalone mode](#running-menu-in-standalone-mode) - [Developer mode (Uploading ROMs from the PC, and more)](#developer-mode-uploading-roms-from-the-pc-and-more)
- [Uploading game and/or save from PC](#uploading-game-andor-save-from-pc) - [Uploading game and/or save from PC](#uploading-game-andor-save-from-pc)
- [Downloading save to PC](#downloading-save-to-pc) - [Downloading save to PC](#downloading-save-to-pc)
- [Running 64DD games from PC](#running-64dd-games-from-pc) - [Running 64DD games from PC](#running-64dd-games-from-pc)
- [Direct boot option](#direct-boot-option) - [Direct boot option](#direct-boot-option)
- [Debug terminal on PC](#debug-terminal-on-pc) - [Debug terminal on PC](#debug-terminal-on-pc)
- [Firmware backup/update](#firmware-backupupdate)
- [LED blink patters](#led-blink-patters) - [LED blink patters](#led-blink-patters)
--- ---
## First time setup on PC # First time setup
**Windows platform: replace `./sc64deployer` in examples below with `sc64deployer.exe`** ## Standalone mode (Running menu and games on the N64)
1. Download the latest deployer tool (`sc64-deployer-{os}-{version}.{ext}`) and firmware (`sc64-firmware-{version}.bin`) from GitHub releases page
2. Extract deployer tool package contents to a folder and place firmware file inside it
3. Connect SC64 device to your computer with USB type C cable
4. Run `./sc64deployer list` to check if device is detected in the system
5. Update SC64 firmware to the latest version with `./sc64deployer firmware update sc64-firmware-{version}.bin`
6. Run `./sc64deployer info` to check if update process finished successfully and SC64 is detected correctly
---
## Firmware backup/update
Keeping SC64 firmware up to date is highly recommended.
`sc64deployer` application is tightly coupled with specific firmware versions and will error out when it detects unsupported firmware version.
To download and backup current version of the SC64 firmware run `./sc64deployer firmware backup sc64-firmware-backup.bin`
To update SC64 firmware run `./sc64deployer firmware update sc64-firmware-{version}.bin`
To print firmware metadata run `./sc64deployer firmware info sc64-firmware-{version}.bin`
---
## Running menu in standalone mode
Menu, as known from 64drive or EverDrive-64, is developed in another repository: [N64FlashcartMenu](https://github.com/Polprzewodnikowy/N64FlashcartMenu). Menu, as known from 64drive or EverDrive-64, is developed in another repository: [N64FlashcartMenu](https://github.com/Polprzewodnikowy/N64FlashcartMenu).
Download latest version from [here](https://github.com/Polprzewodnikowy/N64FlashcartMenu/releases) and put `sc64menu.n64` file in the root directory of the SD card. Download latest version from [here](https://github.com/Polprzewodnikowy/N64FlashcartMenu/releases) and put `sc64menu.n64` file in the root directory of the SD card.
Additionally, follow the instructions in the N64FlashcartMenu repository for more information about thr SD card setup and extra functionality.
When N64 is powered on menu is automatically loaded from the SD card. Supported file system formats are FAT32 and exFAT. When N64 is powered on menu is automatically loaded from the SD card. Supported file system formats are FAT32 and exFAT.
--- ---
## Uploading game and/or save from PC ## Developer mode (Uploading ROMs from the PC, and more)
**Windows platform: replace `./sc64deployer` in examples below with `sc64deployer.exe`**
1. Download the latest deployer tool (`sc64-deployer-{os}-{version}.{ext}`) from the GitHub releases page
2. Extract deployer tool package contents to a folder
3. Connect SC64 device to your computer with USB type C cable
4. Run `./sc64deployer list` to check if device is detected in the system
5. Follow instructions below for specific use cases
### Uploading game and/or save from PC
`./sc64deployer upload path_to_rom.n64 --save-type eeprom4k --save path_to_save.sav` `./sc64deployer upload path_to_rom.n64 --save-type eeprom4k --save path_to_save.sav`
@ -53,18 +41,14 @@ Application will try to autodetect used save type so explicitly setting save typ
Check included help in the application to list available save types. Check included help in the application to list available save types.
Arguments `--save-type` and/or `--save` can be omitted if game doesn't require any save or you want to start with fresh save file. Arguments `--save-type` and/or `--save` can be omitted if game doesn't require any save or you want to start with fresh save file.
--- ### Downloading save to PC
## Downloading save to PC
`./sc64deployer download save path_to_save.sav` `./sc64deployer download save path_to_save.sav`
Replace `path_to_save.sav` with appropriate value. Replace `path_to_save.sav` with appropriate value.
Command will raise error when no save type is currently enabled in the SC64 device. Command will raise error when no save type is currently enabled in the SC64 device.
--- ### Running 64DD games from PC
## Running 64DD games from PC
64DD games require DDIPL ROM and disk images. 64DD games require DDIPL ROM and disk images.
To run disk game type `./sc64deployer 64dd path_to_ddipl.n64 path_to_disk_1.ndd path_to_disk_2.ndd`. To run disk game type `./sc64deployer 64dd path_to_ddipl.n64 path_to_disk_1.ndd path_to_disk_2.ndd`.
@ -79,31 +63,38 @@ Make sure retail and development disks formats aren't mixed together.
If disk game supports running in conjunction with cartridge game then `--rom path_to_rom.n64` argument can be added to command above. If disk game supports running in conjunction with cartridge game then `--rom path_to_rom.n64` argument can be added to command above.
N64 will boot cartridge game instead of 64DD IPL. N64 will boot cartridge game instead of 64DD IPL.
--- ### Direct boot option
## Direct boot option
If booting game through included bootloader isn't a desired option then flashcart can be put in special mode that omits this step. If booting game through included bootloader isn't a desired option then flashcart can be put in special mode that omits this step.
Pass `--direct` option in `upload` or `64dd` command to disable bootloader during boot and console reset. Pass `--direct` option in `upload` or `64dd` command to disable bootloader during boot and console reset.
This option is useful only for very specific cases (e.g. testing custom IPL3 or running SC64 on top of GameShark). This option is useful only for very specific cases (e.g. testing custom IPL3 or running SC64 on top of GameShark).
TV type cannot be forced when direct boot mode is enabled. TV type cannot be forced when direct boot mode is enabled.
--- ### Debug terminal on PC
## Debug terminal on PC
`sc64deployer` application supports UNFLoader protocol and has same functionality implemented as aforementioned program. `sc64deployer` application supports UNFLoader protocol and has same functionality implemented as aforementioned program.
Type `./sc64deployer debug` to activate it. Type `./sc64deployer debug` to activate it.
### Firmware backup/update
Keeping SC64 firmware up to date is strongly recommended.
`sc64deployer` application is tightly coupled with specific firmware versions and will error out when it detects unsupported firmware version.
To download and backup current version of the SC64 firmware run `./sc64deployer firmware backup sc64-firmware-backup.bin`
To update SC64 firmware run `./sc64deployer firmware update sc64-firmware-{version}.bin`
To print firmware metadata run `./sc64deployer firmware info sc64-firmware-{version}.bin`
--- ---
## LED blink patters # LED blink patters
LED on SC64 board can blink in certain situations. Most of them during normal use are related to SD card access. Here's list of blink patterns: LED on SC64 board can blink in certain situations. Most of them during normal use are related to SD card access. Here's list of blink patterns:
| Pattern | Meaning | | Pattern | Meaning |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------ | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
| Nx [Short ON - Short OFF] | SD card access is in progress (initialization or data read/write) or save writeback is in progress | | Irregular | SD card access is in progress (initialization or data read/write) or save writeback was finished |
| Nx [Medium ON - Long OFF] | CIC region did not match, please power off console and power on again | | Nx [Medium ON - Long OFF] | CIC region did not match, please power off console and power on again |
| 2x [Very short ON - Short OFF] | Pattern used during firmware update process, it means that specific part of firmware has started programming | | 2x [Very short ON - Short OFF] | Pattern used during firmware update process, it means that specific part of firmware has started programming |
| 10x [Very short ON - Very short OFF] | Firmware has been successfully updated | | 10x [Very short ON - Very short OFF] | Firmware has been successfully updated |
@ -112,4 +103,4 @@ LED on SC64 board can blink in certain situations. Most of them during normal us
Nx means that blink count is varied. Nx means that blink count is varied.
LED blinking on SD card access can be disabled through `sc64deployer` application. LED blinking on SD card access can be disabled through `sc64deployer` application.
Please refer to included help for option to change the LED behavior. Please refer to the included help for option to change the LED behavior.

View File

@ -1,17 +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_0018`: **AUX**](#0x1fff_0018-aux)
- [Command execution flow](#command-execution-flow) - [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. This mapping is used internally by FPGA/μC and when accessing flashcart from USB side.
@ -21,17 +25,17 @@ This mapping is used internally by FPGA/μC and when accessing flashcart from US
| Flash [1] | `0x0400_0000` | 16 MiB | RW/R | Flash | | Flash [1] | `0x0400_0000` | 16 MiB | RW/R | Flash |
| Data buffer | `0x0500_0000` | 8 kiB | RW | BlockRAM | | Data buffer | `0x0500_0000` | 8 kiB | RW | BlockRAM |
| EEPROM | `0x0500_2000` | 2 kiB | RW | BlockRAM | | EEPROM | `0x0500_2000` | 2 kiB | RW | BlockRAM |
| 64DD buffer | `0x0500_2800` | 256 bytes | RW | BlockRAM | | 64DD/MCU buffer | `0x0500_2800` | 1 kiB | RW | BlockRAM |
| FlashRAM buffer [2] | `0x0500_2900` | 128 bytes | R | BlockRAM | | FlashRAM buffer [2] | `0x0500_2C00` | 128 bytes | R | BlockRAM |
| N/A [3] | `0x0500_2980` | to `0x07FF_FFFF` | R | N/A | | N/A [3] | `0x0500_2C80` | to `0x07FF_FFFF` | R | N/A |
- 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 [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 [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. This mapping is used when accessing flashcart from N64 side.
@ -49,13 +53,13 @@ This mapping is used when accessing flashcart from N64 side.
| ROM shadow [7] | `0x1FFC_0000` | 128 kiB | R | `0x04FE_0000` | Flash | mem bus | SC64 register access is enabled | | ROM shadow [7] | `0x1FFC_0000` | 128 kiB | R | `0x04FE_0000` | Flash | mem bus | SC64 register access is enabled |
| Data buffer | `0x1FFE_0000` | 8 kiB | RW | `0x0500_0000` | Block RAM | mem bus | SC64 register access is enabled | | Data buffer | `0x1FFE_0000` | 8 kiB | RW | `0x0500_0000` | Block RAM | mem bus | SC64 register access is enabled |
| EEPROM | `0x1FFE_2000` | 2 kiB | RW | `0x0500_2000` | Block RAM | mem bus | SC64 register access is enabled | | 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 | | 64DD/MCU buffer [8] | `0x1FFE_2800` | 1 kiB | 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 | | FlashRAM buffer [8] | `0x1FFE_2C00` | 128 bytes | R | `0x0500_2C00` | 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 [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 [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 [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 [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. - 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.
@ -80,59 +84,69 @@ 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 diferrent 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 and IRQ clear | | **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 | W | IRQ clear and enable |
| **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 |
| `IRQ_PENDING` | [29] | R | `1` if flashcart has raised an interrupt | | `BTN_IRQ_PENDING` | [29] | R | `1` if flashcart has raised a "button pressed" interrupt |
| N/A | [28:8] | N/A | Unused, write `0` for future compatibility | | `BTN_IRQ_MASK` | [28] | R | `1` means "button pressed" interrupt is enabled (always `1`) |
| `CMD_ID` | [7:0] | RW | Command ID to be executed | | `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. 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 |
| ------------ | ------ | ------ | ------------------------------------------------- | | ------------ | ------ | ------ | ----------------------------------- |
| `IDENTIFIER` | [31:0] | RW | Flashcart identifier (ASCII `SCv2`) and IRQ clear | | `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 | | name | bits | access | meaning |
| ----- | ------ | ------ | ------------------------------------------ | | ----- | ------ | ------ | ------------------------------------------ |
@ -140,19 +154,86 @@ Note: Writing any value to this register will clear pending flashcart interrupt.
Note: By default from cold boot (power on) or console reset (NMI) flashcart will disable access to SC64 specific memory regions. 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. **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. 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. Value `0xFFFFFFFF` will lock all SC64 registers if flashcart is in unlock state.
--- ---
## Command execution flow ### `0x1FFF_0014`: **IRQ**
1. Check if command is already executing by reading `CMD_BUSY` bit in **STATUS/COMMAND** register (optional). | 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. 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 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 **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 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 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.

View File

@ -4,27 +4,28 @@
## N64 commands ## N64 commands
| id | name | arg0 | arg1 | rsp0 | rsp1 | description | | id | name | arg0 | arg1 | rsp0 | rsp1 | description |
| --- | --------------------- | -------------- | ------------ | ---------------- | -------------- | ---------------------------------------------------------- | | --- | --------------------- | ------------- | ------------ | ---------------- | -------------- | ------------------------------------------------------------ |
| `v` | **IDENTIFIER_GET** | --- | --- | identifier | --- | Get flashcart identifier `SCv2` | | `v` | **IDENTIFIER_GET** | --- | --- | identifier | --- | Get flashcart identifier `SCv2` |
| `V` | **VERSION_GET** | --- | --- | major/minor | revision | Get flashcart firmware version | | `V` | **VERSION_GET** | --- | --- | major/minor | revision | Get flashcart firmware version |
| `c` | **CONFIG_GET** | config_id | --- | --- | current_value | Get config option | | `c` | **CONFIG_GET** | config_id | --- | --- | current_value | Get config option |
| `C` | **CONFIG_SET** | config_id | new_value | --- | previous_value | Set config option and get previous value | | `C` | **CONFIG_SET** | config_id | new_value | --- | previous_value | Set config option and get previous value |
| `a` | **SETTING_GET** | setting_id | --- | --- | current_value | Get persistent setting option | | `a` | **SETTING_GET** | setting_id | --- | --- | current_value | Get persistent setting option |
| `A` | **SETTING_SET** | setting_id | new_value | --- | --- | Set persistent setting option | | `A` | **SETTING_SET** | setting_id | new_value | --- | --- | Set persistent setting option |
| `t` | **TIME_GET** | --- | --- | time_0 | time_1 | Get current RTC value | | `t` | **TIME_GET** | --- | --- | time_0 | time_1 | Get current RTC value |
| `T` | **TIME_SET** | time_0 | time_1 | --- | --- | Set new RTC value | | `T` | **TIME_SET** | time_0 | time_1 | --- | --- | Set new RTC value |
| `m` | **USB_READ** | pi_address | length | --- | --- | Receive data from USB to flashcart | | `m` | **USB_READ** | pi_address | length | --- | --- | Receive data from USB to flashcart |
| `M` | **USB_WRITE** | pi_address | length/type | --- | --- | Send data from from flashcart to USB | | `M` | **USB_WRITE** | pi_address | length/type | --- | --- | Send data from from flashcart to USB |
| `u` | **USB_READ_STATUS** | --- | --- | read_status/type | length | Get USB read status and type/length | | `u` | **USB_READ_STATUS** | --- | --- | read_status/type | length | Get USB read status and type/length |
| `U` | **USB_WRITE_STATUS** | --- | --- | write_status | --- | Get USB write status | | `U` | **USB_WRITE_STATUS** | --- | --- | write_status | --- | Get USB write status |
| `i` | **SD_CARD_OP** | pi_address | operation | --- | return_data | Perform special operation on SD card | | `i` | **SD_CARD_OP** | pi_address | operation | --- | return_data | Perform special operation on the SD card |
| `I` | **SD_SECTOR_SET** | sector | --- | --- | --- | Set starting sector for next SD card R/W operation | | `I` | **SD_SECTOR_SET** | sector | --- | --- | --- | Set starting sector for next SD card R/W operation |
| `s` | **SD_READ** | pi_address | sector_count | --- | --- | Read sectors from SD card to flashcart | | `s` | **SD_READ** | pi_address | sector_count | --- | --- | Read sectors from the SD card to flashcart memory space |
| `S` | **SD_WRITE** | pi_address | sector_count | --- | --- | Write sectors from flashcart to SD card | | `S` | **SD_WRITE** | pi_address | sector_count | --- | --- | Write sectors from the flashcart memory space to the SD card |
| `D` | **DISK_MAPPING_SET** | pi_address | table_size | --- | --- | Set 64DD disk mapping for SD mode | | `D` | **DISK_MAPPING_SET** | pi_address | table_size | --- | --- | Set 64DD disk mapping for SD mode |
| `w` | **WRITEBACK_PENDING** | --- | --- | pending_status | --- | Get save writeback status (is write queued to the SD card) | | `w` | **WRITEBACK_PENDING** | --- | --- | pending_status | --- | Get save writeback status (is write queued to the SD card) |
| `W` | **WRITEBACK_SD_INFO** | pi_address | --- | --- | --- | Load writeback SD sector table and enable it | | `W` | **WRITEBACK_SD_INFO** | pi_address | --- | --- | --- | Load writeback SD sector table and enable it |
| `K` | **FLASH_PROGRAM** | pi_address | length | --- | --- | Program flash with bytes loaded into data buffer | | `K` | **FLASH_PROGRAM** | pi_address | length | --- | --- | Program flash with bytes loaded into data buffer |
| `p` | **FLASH_WAIT_BUSY** | wait | --- | erase_block_size | --- | Wait until flash ready / get block erase size | | `p` | **FLASH_WAIT_BUSY** | wait | --- | erase_block_size | --- | Wait until flash ready / get block erase size |
| `P` | **FLASH_ERASE_BLOCK** | pi_address | --- | --- | --- | Start flash block erase | | `P` | **FLASH_ERASE_BLOCK** | pi_address | --- | --- | --- | Start flash block erase |
| `%` | **DIAGNOSTIC_GET** | diagnostic_id | --- | --- | value | Get diagnostic data |

View File

@ -3,7 +3,7 @@
- [PC -\> SC64 packets](#pc---sc64-packets) - [PC -\> SC64 packets](#pc---sc64-packets)
- [**`CMD`** packet](#cmd-packet) - [**`CMD`** packet](#cmd-packet)
- [SC64 -\> PC packets](#sc64---pc-packets) - [SC64 -\> PC packets](#sc64---pc-packets)
- [**`RSP`/`ERR`** packets](#rsperr-packets) - [**`CMP`/`ERR`** packets](#cmperr-packets)
- [**`PKT`** packet](#pkt-packet) - [**`PKT`** packet](#pkt-packet)
- [Supported commands](#supported-commands) - [Supported commands](#supported-commands)
- [`v`: **IDENTIFIER\_GET**](#v-identifier_get) - [`v`: **IDENTIFIER\_GET**](#v-identifier_get)
@ -43,13 +43,33 @@
- [`arg0` (type)](#arg0-type) - [`arg0` (type)](#arg0-type)
- [`arg1` (length)](#arg1-length-2) - [`arg1` (length)](#arg1-length-2)
- [`data` (data)](#data-data-1) - [`data` (data)](#data-data-1)
- [`X`: **AUX\_WRITE**](#x-aux_write)
- [`arg0` (data)](#arg0-data)
- [`i`: **SD\_CARD\_OP**](#i-sd_card_op)
- [`arg0` (address)](#arg0-address-2)
- [`arg1` (operation)](#arg1-operation)
- [`response` (result/status)](#response-resultstatus)
- [Available SD card operations](#available-sd-card-operations)
- [SD card status](#sd-card-status)
- [`s`: **SD\_READ**](#s-sd_read)
- [`arg0` (address)](#arg0-address-3)
- [`arg1` (sector\_count)](#arg1-sector_count)
- [`data` (sector)](#data-sector)
- [`response` (result)](#response-result)
- [`S`: **SD\_WRITE**](#s-sd_write)
- [`arg0` (address)](#arg0-address-4)
- [`arg1` (sector\_count)](#arg1-sector_count-1)
- [`data` (sector)](#data-sector-1)
- [`response` (result)](#response-result-1)
- [`D`: **DD\_SET\_BLOCK\_READY**](#d-dd_set_block_ready) - [`D`: **DD\_SET\_BLOCK\_READY**](#d-dd_set_block_ready)
- [`arg0` (error)](#arg0-error) - [`arg0` (error)](#arg0-error)
- [`W`: **WRITEBACK\_ENABLE**](#w-writeback_enable) - [`W`: **WRITEBACK\_ENABLE**](#w-writeback_enable)
- [Asynchronous packets](#asynchronous-packets) - [Asynchronous packets](#asynchronous-packets)
- [`X`: **AUX\_DATA**](#x-aux_data)
- [`data` (data)](#data-data-2)
- [`B`: **BUTTON**](#b-button) - [`B`: **BUTTON**](#b-button)
- [`U`: **DATA**](#u-data) - [`U`: **DATA**](#u-data)
- [`data` (data)](#data-data-2) - [`data` (data)](#data-data-3)
- [`G`: **DATA\_FLUSHED**](#g-data_flushed) - [`G`: **DATA\_FLUSHED**](#g-data_flushed)
- [`D`: **DISK\_REQUEST**](#d-disk_request) - [`D`: **DISK\_REQUEST**](#d-disk_request)
- [`data` (disk\_info/block\_data)](#data-disk_infoblock_data) - [`data` (disk\_info/block\_data)](#data-disk_infoblock_data)
@ -108,29 +128,29 @@ Packet data length is derived from the argument if specific command supports it.
| identifier | description | | identifier | description |
| ---------- | ---------------------------------------- | | ---------- | ---------------------------------------- |
| `RSP` | Success response to the received command | | `CMP` | Success response to the received command |
| `ERR` | Error response to the received command | | `ERR` | Error response to the received command |
| `PKT` | Asynchronous data packet | | `PKT` | Asynchronous data packet |
SC64 sends response packet `RSP`/`ERR` to almost every command received from the PC. SC64 sends response packet `CMP`/`ERR` to almost every command received from the PC.
Fourth byte is the same as in the command that triggered the response. Fourth byte is the same as in the command that triggered the response.
If command execution was not successful, then `RSP` identifier is replaced by the `ERR` identifier. If command execution was not successful, then `CMP` identifier is replaced by the `ERR` identifier.
SC64 can also send `PKT` packet at any time as a response to action triggered by the N64 or the flashcart itself. SC64 can also send `PKT` packet at any time as a response to action triggered by the N64 or the flashcart itself.
Fourth byte denotes packet ID listed in the [asynchronous packets](#asynchronous-packets) section. Fourth byte denotes packet ID listed in the [asynchronous packets](#asynchronous-packets) section.
#### **`RSP`/`ERR`** packets #### **`CMP`/`ERR`** packets
General structure of packet: General structure of packet:
| offset | type | description | | offset | type | description |
| ------ | -------------------- | ---------------------- | | ------ | -------------------- | ---------------------- |
| `0` | char[3] | `RSP`/`ERR` identifier | | `0` | char[3] | `CMP`/`ERR` identifier |
| `3` | char[1] | Command ID | | `3` | char[1] | Command ID |
| `4` | uint32_t | Response data length | | `4` | uint32_t | Response data length |
| `8` | uint8_t[data_length] | Response data (if any) | | `8` | uint8_t[data_length] | Response data (if any) |
`RSP`/`ERR` packet is sent as a response to the command sent by the PC. `CMP`/`ERR` packet is sent as a response to the command sent by the PC.
`ERR` response might contain no (or undefined) data in the arbitrary data field compared to regular `RSP` packet. `ERR` response might contain no (or undefined) data in the arbitrary data field compared to regular `CMP` packet.
#### **`PKT`** packet #### **`PKT`** packet
@ -148,29 +168,33 @@ Available packet IDs are listed in the [asynchronous packets](#asynchronous-pack
## Supported commands ## Supported commands
| id | name | arg0 | arg1 | data | response | description | | id | name | arg0 | arg1 | data | response | description |
| --- | ----------------------------------------------- | ------------ | ------------- | ---- | ---------------- | ------------------------------------------------------------- | | --- | ----------------------------------------------- | ------------ | ------------- | ------ | ---------------- | -------------------------------------------------------------- |
| `v` | [**IDENTIFIER_GET**](#v-identifier_get) | --- | --- | --- | identifier | Get flashcart identifier `SCv2` | | `v` | [**IDENTIFIER_GET**](#v-identifier_get) | --- | --- | --- | identifier | Get flashcart identifier `SCv2` |
| `V` | [**VERSION_GET**](#v-version_get) | --- | --- | --- | version | Get flashcart firmware version | | `V` | [**VERSION_GET**](#v-version_get) | --- | --- | --- | version | Get flashcart firmware version |
| `R` | [**STATE_RESET**](#r-state_reset) | --- | --- | --- | --- | Reset flashcart state (CIC params and config options) | | `R` | [**STATE_RESET**](#r-state_reset) | --- | --- | --- | --- | Reset flashcart state (CIC params and config options) |
| `B` | [**CIC_PARAMS_SET**](#b-cic_params_set) | cic_params_0 | cic_params_1 | --- | --- | Set CIC emulation parameters (disable/seed/checksum) | | `B` | [**CIC_PARAMS_SET**](#b-cic_params_set) | cic_params_0 | cic_params_1 | --- | --- | Set CIC emulation parameters (disable/seed/checksum) |
| `c` | [**CONFIG_GET**](#c-config_get) | config_id | --- | --- | config_value | Get config option | | `c` | [**CONFIG_GET**](#c-config_get) | config_id | --- | --- | config_value | Get config option |
| `C` | [**CONFIG_SET**](#c-config_set) | config_id | config_value | --- | --- | Set config option | | `C` | [**CONFIG_SET**](#c-config_set) | config_id | config_value | --- | --- | Set config option |
| `a` | [**SETTING_GET**](#a-setting_get) | setting_id | --- | --- | setting_value | Get persistent setting option | | `a` | [**SETTING_GET**](#a-setting_get) | setting_id | --- | --- | setting_value | Get persistent setting option |
| `A` | [**SETTING_SET**](#a-setting_set) | setting_id | setting_value | --- | --- | Set persistent setting option | | `A` | [**SETTING_SET**](#a-setting_set) | setting_id | setting_value | --- | --- | Set persistent setting option |
| `t` | [**TIME_GET**](#t-time_get) | --- | --- | --- | time | Get current RTC value | | `t` | [**TIME_GET**](#t-time_get) | --- | --- | --- | time | Get current RTC value |
| `T` | [**TIME_SET**](#t-time_set) | time_0 | time_1 | --- | --- | Set new RTC value | | `T` | [**TIME_SET**](#t-time_set) | time_0 | time_1 | --- | --- | Set new RTC value |
| `m` | [**MEMORY_READ**](#m-memory_read) | address | length | --- | data | Read data from specified memory address | | `m` | [**MEMORY_READ**](#m-memory_read) | address | length | --- | data | Read data from specified memory address |
| `M` | [**MEMORY_WRITE**](#m-memory_write) | address | length | data | --- | Write data to specified memory address | | `M` | [**MEMORY_WRITE**](#m-memory_write) | address | length | data | --- | Write data to specified memory address |
| `U` | [**USB_WRITE**](#u-usb_write) | type | length | data | N/A | Send data to be received by app running on N64 (no response!) | | `U` | [**USB_WRITE**](#u-usb_write) | type | length | data | N/A | Send data to be received by app running on N64 (no response!) |
| `D` | [**DD_SET_BLOCK_READY**](#d-dd_set_block_ready) | error | --- | --- | --- | Notify flashcart about 64DD block readiness | | `X` | [**AUX_WRITE**](#x-aux_write) | data | --- | --- | --- | Send small auxiliary data to be received by app running on N64 |
| `W` | [**WRITEBACK_ENABLE**](#w-writeback_enable) | --- | --- | --- | --- | Enable save writeback through USB packet | | `i` | [**SD_CARD_OP**](#i-sd_card_op) | address | operation | --- | result/status | Perform special operation on the SD card |
| `p` | **FLASH_WAIT_BUSY** | wait | --- | --- | erase_block_size | Wait until flash ready / Get flash block erase size | | `s` | [**SD_READ**](#s-sd_read) | address | sector_count | sector | result | Read sectors from the SD card to flashcart memory space |
| `P` | **FLASH_ERASE_BLOCK** | address | --- | --- | --- | Start flash block erase | | `S` | [**SD_WRITE**](#s-sd_write) | address | sector_count | sector | result | Write sectors from the flashcart memory space to the SD card |
| `f` | **FIRMWARE_BACKUP** | address | --- | --- | status/length | Backup firmware to specified memory address | | `D` | [**DD_SET_BLOCK_READY**](#d-dd_set_block_ready) | error | --- | --- | --- | Notify flashcart about 64DD block readiness |
| `F` | **FIRMWARE_UPDATE** | address | length | --- | status | Update firmware from specified memory address | | `W` | [**WRITEBACK_ENABLE**](#w-writeback_enable) | --- | --- | --- | --- | Enable save writeback through USB packet |
| `?` | **DEBUG_GET** | --- | --- | --- | debug_data | Get internal FPGA debug info | | `p` | **FLASH_WAIT_BUSY** | wait | --- | --- | erase_block_size | Wait until flash ready / Get flash block erase size |
| `%` | **STACK_USAGE_GET** | --- | --- | --- | stack_usage | Get per task stack usage | | `P` | **FLASH_ERASE_BLOCK** | address | --- | --- | --- | Start flash block erase |
| `f` | **FIRMWARE_BACKUP** | address | --- | --- | status/length | Backup firmware to specified memory address |
| `F` | **FIRMWARE_UPDATE** | address | length | --- | status | Update firmware from specified memory address |
| `?` | **DEBUG_GET** | --- | --- | --- | debug_data | Get internal FPGA debug info |
| `%` | **DIAGNOSTIC_GET** | --- | --- | --- | diagnostic_data | Get diagnostic data |
--- ---
@ -218,7 +242,7 @@ _This command does not require arguments or data._
_This command does not send response data._ _This command does not send response data._
This command is used to reset most of the config options to default state (same as on power-up). This command is used to reset most of the config options to default state (same as on power-up).
Additionally, CIC emulation is enabled and 6102/7101 seed/checksum values are set. Additionally, CIC emulation is enabled, 6102/7101 seed/checksum values are set and SD card lock is released.
--- ---
@ -328,16 +352,16 @@ Use this command to set value of persistent setting option. Available persistent
_This command does not require arguments or data._ _This command does not require arguments or data._
#### `response` (time) #### `response` (time)
| offset | type | description | | offset | type | description |
| ------ | ------- | ------------------------------------ | | ------ | ------- | --------------------------------------- |
| `0` | uint8_t | Weekday (1 - 7), 1 represents Monday | | `0` | uint8_t | Weekday (1 - 7), 1 represents Monday |
| `1` | uint8_t | Hours (0 - 23) | | `1` | uint8_t | Hours (0 - 23) |
| `2` | uint8_t | Minutes (0 - 59) | | `2` | uint8_t | Minutes (0 - 59) |
| `3` | uint8_t | Seconds (0 - 59) | | `3` | uint8_t | Seconds (0 - 59) |
| `4` | uint8_t | _Unused_ (returns zero) | | `4` | uint8_t | Century (0 - 7), 0 represents year 1900 |
| `5` | uint8_t | Year (0 - 99) | | `5` | uint8_t | Year (0 - 99) |
| `6` | uint8_t | Month (1 - 12) | | `6` | uint8_t | Month (1 - 12) |
| `7` | uint8_t | Day (1 - 31) | | `7` | uint8_t | Day (1 - 31) |
Date/time values use the [BCD](https://en.wikipedia.org/wiki/Binary-coded_decimal) format. Date/time values use the [BCD](https://en.wikipedia.org/wiki/Binary-coded_decimal) format.
@ -356,12 +380,12 @@ Date/time values use the [BCD](https://en.wikipedia.org/wiki/Binary-coded_decima
| `[7:0]` | Seconds (0 - 59) | | `[7:0]` | Seconds (0 - 59) |
#### `arg1` (time_1) #### `arg1` (time_1)
| bits | description | | bits | description |
| --------- | -------------- | | --------- | --------------------------------------- |
| `[31:24]` | _Unused_ | | `[31:24]` | Century (0 - 7), 0 represents year 1900 |
| `[23:16]` | Year (0 - 99) | | `[23:16]` | Year (0 - 99) |
| `[15:8]` | Month (1 - 12) | | `[15:8]` | Month (1 - 12) |
| `[7:0]` | Day (1 - 31) | | `[7:0]` | Day (1 - 31) |
_This command does not send response data._ _This command does not send response data._
@ -439,7 +463,7 @@ Writes bytes to the specified memory address. Please refer to the [internal memo
_This command does not send response data._ _This command does not send response data._
_This command does not send `RSP`/`ERR` packet response!_ _This command does not send `CMP`/`ERR` packet response!_
This command notifies N64 that data is waiting to be acknowledged. This command notifies N64 that data is waiting to be acknowledged.
If N64 side doesn't acknowledge data via [`m` **USB_READ**](./02_n64_commands.md#m-usb_read) N64 command within 1 second then data is flushed and [`G` **DATA_FLUSHED**](#asynchronous-packets) asynchronous packet is sent to the PC. If N64 side doesn't acknowledge data via [`m` **USB_READ**](./02_n64_commands.md#m-usb_read) N64 command within 1 second then data is flushed and [`G` **DATA_FLUSHED**](#asynchronous-packets) asynchronous packet is sent to the PC.
@ -447,6 +471,125 @@ If N64 acknowledge the request, then data is written to the flashcart memory to
--- ---
### `X`: **AUX_WRITE**
**Send small auxiliary data to be received by app running on N64**
#### `arg0` (data)
| bits | description |
| -------- | ----------- |
| `[31:0]` | Data |
_This command does not send response data._
This command puts 32 bits of data to the AUX register accessible from the N64 side, and generates cart interrupt (if enabled).
---
### `i`: **SD_CARD_OP**
**Perform special operation on the SD card**
#### `arg0` (address)
| bits | description |
| -------- | ----------- |
| `[31:0]` | Address |
#### `arg1` (operation)
| bits | description |
| -------- | ----------- |
| `[31:0]` | Operation |
#### `response` (result/status)
| offset | type | description |
| ------ | -------- | ---------------------------------------------------------------------------------------------- |
| `0` | uint32_t | Operation result (valid values are listed in the [sd_error_t](../sw/controller/src/sd.h) enum) |
| `4` | uint32_t | SD card status (always returned and valid regardless of the SD card operation result) |
This command performs special operation on the SD card. When operation result is not `SD_OK`, then `ERR` packet is returned.
PC and N64 cannot use the SD card interface at the same time. Lock mechanism is implemented to prevent data corruption.
SD card access is locked when PC or N64 requests SD card initialization, and unlocked when the card is uninitialized.
Lock is also released when certain conditions occur - when N64 side lock is active then it is released when the console is powered off
and save writeback operation is not pending. PC side lock is released when USB interface is in reset state, or the USB cable is unplugged.
#### Available SD card operations
| operation | description |
| --------- | ---------------------------------------------------------------------------------------------- |
| `0` | Init SD card |
| `1` | Deinit SD card |
| `2` | Get SD card status |
| `3` | Get SD card info (loads CSD and CID registers to a specified address, data length is 32 bytes) |
| `4` | Turn on byte swap |
| `5` | Turn off byte swap |
#### SD card status
| bits | description |
| -------- | -------------------------------------------------------------------------- |
| `[31:5]` | _Unused_ |
| `[4]` | `0` - Byte swap disabled, `1` - Byte swap enabled (valid when initialized) |
| `[3]` | `0` - 25 MHz clock, `1` - 50 MHz clock (valid when initialized) |
| `[2]` | `0` - Byte addressed, `1` - Sector addressed (valid when initialized) |
| `[1]` | `0` - SD card not initialized, `1` - SD card initialized |
| `[0]` | `0` - SD card not inserted, `1` - SD card inserted |
---
### `s`: **SD_READ**
**Read sectors from the SD card to flashcart memory space**
#### `arg0` (address)
| bits | description |
| -------- | ----------- |
| `[31:0]` | Address |
#### `arg1` (sector_count)
| bits | description |
| -------- | ------------ |
| `[31:0]` | Sector count |
#### `data` (sector)
| offset | type | description |
| ------ | -------- | --------------- |
| `0` | uint32_t | Starting sector |
#### `response` (result)
| offset | type | description |
| ------ | -------- | ---------------------------------------------------------------------------------------------- |
| `0` | uint32_t | Operation result (valid values are listed in the [sd_error_t](../sw/controller/src/sd.h) enum) |
This command reads sectors from the SD card to a specified memory address. When operation result is not `SD_OK`, then `ERR` packet is returned.
---
### `S`: **SD_WRITE**
**Write sectors from the flashcart memory space to the SD card**
#### `arg0` (address)
| bits | description |
| -------- | ----------- |
| `[31:0]` | Address |
#### `arg1` (sector_count)
| bits | description |
| -------- | ------------ |
| `[31:0]` | Sector count |
#### `data` (sector)
| offset | type | description |
| ------ | -------- | --------------- |
| `0` | uint32_t | Starting sector |
#### `response` (result)
| offset | type | description |
| ------ | -------- | ---------------------------------------------------------------------------------------------- |
| `0` | uint32_t | Operation result (valid values are listed in the [sd_error_t](../sw/controller/src/sd.h) enum) |
This command writes sectors from a specified memory address to the SD card. When operation result is not `SD_OK`, then `ERR` packet is returned.
---
### `D`: **DD_SET_BLOCK_READY** ### `D`: **DD_SET_BLOCK_READY**
**Notify flashcart about 64DD block readiness** **Notify flashcart about 64DD block readiness**
@ -482,6 +625,7 @@ Save data is sent via [**SAVE_WRITEBACK**](#s-save_writeback) asynchronous packe
| id | name | data | description | | id | name | data | description |
| --- | --------------------------------------- | -------------------- | --------------------------------------------------------------------- | | --- | --------------------------------------- | -------------------- | --------------------------------------------------------------------- |
| `X` | [**AUX_DATA**](#x-aux_data) | data | Data was written to the `AUX` register from the N64 side |
| `B` | [**BUTTON**](#b-button) | --- | Button on the back of the SC64 was pressed | | `B` | [**BUTTON**](#b-button) | --- | Button on the back of the SC64 was pressed |
| `U` | [**DATA**](#u-data) | data | Data sent from the N64 | | `U` | [**DATA**](#u-data) | data | Data sent from the N64 |
| `G` | [**DATA_FLUSHED**](#g-data_flushed) | --- | Data from [`U` **USB_WRITE**](#u-usb_write) USB command was discarded | | `G` | [**DATA_FLUSHED**](#g-data_flushed) | --- | Data from [`U` **USB_WRITE**](#u-usb_write) USB command was discarded |
@ -490,6 +634,20 @@ Save data is sent via [**SAVE_WRITEBACK**](#s-save_writeback) asynchronous packe
| `S` | [**SAVE_WRITEBACK**](#s-save_writeback) | save_contents | Flushed save data | | `S` | [**SAVE_WRITEBACK**](#s-save_writeback) | save_contents | Flushed save data |
| `F` | [**UPDATE_STATUS**](#f-update_status) | progress | Firmware update progress | | `F` | [**UPDATE_STATUS**](#f-update_status) | progress | Firmware update progress |
---
### `X`: **AUX_DATA**
**Data was written to the `AUX` register from the N64 side**
This packet is sent when N64 writes to the `AUX` register in the SC64 register block.
#### `data` (data)
| offset | type | description |
| ------ | -------- | ----------- |
| `0` | uint32_t | Data |
--- ---
### `B`: **BUTTON** ### `B`: **BUTTON**

View File

@ -133,9 +133,10 @@ type: *enum* | default: `0`
- `4` - FlashRAM 1 Mib save is enabled - `4` - FlashRAM 1 Mib save is enabled
- `5` - SRAM 768 kib save is enabled - `5` - SRAM 768 kib save is enabled
- `6` - SRAM 1 Mib save is enabled - `6` - SRAM 1 Mib save is enabled
- `7` - FakeFlashRAM 1 Mib save is enabled (write/erase timings are not emulated and erase before write is not required)
Use this setting for selecting save type that will be emulated. Only one save type can be enabled. Use this setting for selecting save type that will be emulated. Only one save type can be enabled.
Any successful write to this config will disable automatic save writeback to USB or SD card when previously enabled. Any successful write to this config will disable automatic save writeback to the USB or SD card if previously enabled.
--- ---

View File

@ -10,13 +10,14 @@
Docker method is a preferred option. Docker method is a preferred option.
Run `./docker_build.sh release` to build all firmware/software and generate release package. Run `./docker_build.sh release` to build all firmware/software and generate release package.
For other options run script without any command to print help. For other options run script without any command to print help about available options.
#### Lattice Diamond license #### Lattice Diamond license
Lattice Diamond software is used to build FPGA bitstream, a free 1 year license is necessary to run the build process. Lattice Diamond software is used to build the FPGA bitstream.
Repository already contains license attached to fake MAC address `F8:12:34:56:78:90` (path to the license: `fw/project/lcmxo2/license.dat`). A free 1 year license is necessary to run the build process.
If the license expires, it is required to request new license from Lattice webpage. You can request personal license from the [Lattice website](https://www.latticesemi.com/Support/Licensing).
New license can be attached to aforementioned MAC address or any other address. Build script expects license file to be present in this path: `fw/project/lcmxo2/license.dat`.
In case of non default MAC address it is possible to provide it via `MAC_ADDRESS` environment variable. Since build is done inside docker container it is required to pass the MAC address, linked with the license file, to a container.
For example: `MAC_ADDRESS=AB:00:00:00:00:00 ./docker_build.sh release`. Build script expects it in the `MAC_ADDRESS` environment variable.
For example, run `MAC_ADDRESS=AB:00:00:00:00:00 ./docker_build.sh release` command to start the building process if your license is attached to `AB:00:00:00:00:00` MAC address.

View File

@ -1,3 +1,4 @@
- [Video guide](#video-guide)
- [Step by step guide how to make SC64](#step-by-step-guide-how-to-make-sc64) - [Step by step guide how to make SC64](#step-by-step-guide-how-to-make-sc64)
- [**PCB manufacturing data**](#pcb-manufacturing-data) - [**PCB manufacturing data**](#pcb-manufacturing-data)
- [**PCB requirements**](#pcb-requirements) - [**PCB requirements**](#pcb-requirements)
@ -6,10 +7,16 @@
- [**Putting it together**](#putting-it-together) - [**Putting it together**](#putting-it-together)
- [**Initial programming**](#initial-programming) - [**Initial programming**](#initial-programming)
- [**Troubleshooting**](#troubleshooting) - [**Troubleshooting**](#troubleshooting)
- [*`primer.py` threw error on `Bootloader -> SC64 FLASH` step*](#primerpy-threw-error-on-bootloader---sc64-flash-step) - [*`primer.py` threw `No SC64 USB device found` error*](#primerpy-threw-no-sc64-usb-device-found-error)
- [*`primer.py` threw `SDRAM test error...` message*](#primerpy-threw-sdram-test-error-message)
- [*`primer.py` threw other error message*](#primerpy-threw-other-error-message)
--- ---
## Video guide
[![Video guide](https://img.youtube.com/vi/t6hyCFpwqz8/0.jpg)](https://www.youtube.com/watch?v=t6hyCFpwqz8 "How to build and program the SummerCart64")
## Step by step guide how to make SC64 ## Step by step guide how to make SC64
All necessary manufacturing files are packaged in every `sc64-extra-{version}.zip` file in GitHub releases. All necessary manufacturing files are packaged in every `sc64-extra-{version}.zip` file in GitHub releases.
@ -40,7 +47,7 @@ Please download latest release before proceeding with the instructions.
### **Components** ### **Components**
1. Locate interactive BOM file inside `hw/pcb` folder 1. Locate the interactive BOM file inside `hw/pcb` folder
2. Order all parts listed in the BOM file or use PCB assembly service together with your PCB order 2. Order all parts listed in the BOM file or use PCB assembly service together with your PCB order
--- ---
@ -49,7 +56,7 @@ Please download latest release before proceeding with the instructions.
1. Locate `.stl` files inside `hw/shell` folder 1. Locate `.stl` files inside `hw/shell` folder
2. Use these files in the slicer and 3D printer of your choice or order ready made prints from 3D printing company 2. Use these files in the slicer and 3D printer of your choice or order ready made prints from 3D printing company
3. Find matching screws, go to discussions tab for community recommendations 3. For screws, M2x10 works great for the top two holes while M2x8 fits well in the bottom two
--- ---
@ -73,6 +80,7 @@ You will require the following hardware:
- A USB to UART (serial) adapter (with 3.3V signaling is required, e.g. TTL-232R-3V3). - A USB to UART (serial) adapter (with 3.3V signaling is required, e.g. TTL-232R-3V3).
You will require the following applications and packages: You will require the following applications and packages:
- [Latest FTDI drivers](https://ftdichip.com/drivers/)
- [FT_PROG](https://ftdichip.com/utilities/#ft_prog) - FTDI FT232H EEPROM programming software. - [FT_PROG](https://ftdichip.com/utilities/#ft_prog) - FTDI FT232H EEPROM programming software.
- [Python 3](https://www.python.org/downloads/) with `pip3` - necessary for initial programming script: `primer.py` (Windows install: check option add python to PATH). - [Python 3](https://www.python.org/downloads/) with `pip3` - necessary for initial programming script: `primer.py` (Windows install: check option add python to PATH).
- [`sc64-extra-{version}.zip`](https://github.com/Polprzewodnikowy/SummerCart64/releases) - programming scripts and firmware files, download the latest version. - [`sc64-extra-{version}.zip`](https://github.com/Polprzewodnikowy/SummerCart64/releases) - programming scripts and firmware files, download the latest version.
@ -80,10 +88,11 @@ You will require the following applications and packages:
**Programming must be done in specific order for `primer.py` script to work correctly.** **Programming must be done in specific order for `primer.py` script to work correctly.**
Preparations: Preparations:
1. Install FT_PROG. 1. Install latest FTDI drivers (especially on Windows 10).
2. Install Python 3. 2. Install FT_PROG.
3. Unpack `sc64-extra-{version}.zip` into a folder. 3. Install Python 3.
4. Open terminal and navigate to the folder you've unpacked SC64 files. 4. Unpack `sc64-extra-{version}.zip` into a folder.
5. Open terminal and navigate to the folder you've unpacked SC64 files.
First, program the ***FT232H EEPROM***: First, program the ***FT232H EEPROM***:
1. Connect the SC64 board to your PC with a USB-C cable. 1. Connect the SC64 board to your PC with a USB-C cable.
@ -115,9 +124,21 @@ Congratulations! Your SC64 flashcart should be ready for use!
### **Troubleshooting** ### **Troubleshooting**
#### *`primer.py` threw error on `Bootloader -> SC64 FLASH` step* #### *`primer.py` threw `No SC64 USB device found` error*
This issue can be attributed to incorrectly programmed FT232H EEPROM in the first programming step. This issue can be attributed to incorrectly programmed FT232H EEPROM in the first programming step.
Check again in `FT_PROG` application if device was configured properly. Check again in `FT_PROG` application if device was configured properly.
Make sure default FTDI drivers are installed for the SC64 in the device manager (only on Windows OS).
Make sure you have correct access to `/dev/ttyUSBx` device and `ftdi_sio` and `usbserial` modules are loaded (only on Linux OS).
#### *`primer.py` threw `SDRAM test error...` message*
This issue shows up when there's a problem with the connection to the SDRAM chip or the chip itself is malfunctioning.
Check for any solder bridges and unconnected pins on U8/U9 chips.
Once FPGA and microcontroller has been programmed successfully `primer.py` script needs to be run in special mode. Once FPGA and microcontroller has been programmed successfully `primer.py` script needs to be run in special mode.
Please use command `python3 primer.py COMx sc64-firmware-{version}.bin --bootloader-only` to try programming bootloader again. Please use command `python3 primer.py COMx sc64-firmware-{version}.bin --bootloader-only` to test SDRAM again and continue bootloader programming process.
#### *`primer.py` threw other error message*
Due to multiple possible causes of the problem it's best to start visually inspecting SC64's board for any defects, like bad solder work or chips soldered backwards.
If visual inspection didn't yield any obvious culprits then next step would be to check if everything is connected correctly.
Check if TX/RX signals aren't swapped and if SC64 is getting power from the USB cable. Best place to check supply voltage are the exposed test pads on the left of U8 chip.

View File

@ -1,4 +1,4 @@
# SummerCart64 - a fully open source Nintendo 64 flashcart # SummerCart64 - a fully open source N64 flashcart
[<img src="../assets/sc64_logo.svg" />](../assets/sc64_logo.svg) [<img src="../assets/sc64_logo.svg" />](../assets/sc64_logo.svg)
## Documentation ## Documentation

View File

@ -1,6 +1,7 @@
.recovery .recovery
*.dir/ *.dir/
*.ccl *.ccl
*.dat
*.dmp *.dmp
*.html *.html
*.ini *.ini
@ -11,6 +12,7 @@
*.tcl *.tcl
*.tpf *.tpf
*.trc *.trc
*.txt
*.xml *.xml
impl*/ impl*/

View File

@ -4,6 +4,8 @@ set -e
source $bindir/diamond_env source $bindir/diamond_env
printf "[$(hostname)]\nSYSTEM=Linux\nCORENUM=$(nproc --all)\n" > $(dirname $0)/multicore.txt
diamondc build.tcl diamondc build.tcl
MINIMUM_FREQ=$(cat impl1/sc64_impl1.twr \ MINIMUM_FREQ=$(cat impl1/sc64_impl1.twr \

View File

@ -1,24 +0,0 @@
FEATURE LSC_BASE lattice 8.0 11-feb-2024 uncounted 1303CFE67866 \
VENDOR_STRING="ispLEVER BASE" HOSTID=f81234567890
FEATURE LSC_SYNPLIFY lattice 8.0 11-feb-2024 uncounted B52A4FF71CC8 \
VENDOR_STRING="ispLEVER System with Synplicity" \
HOSTID=f81234567890
FEATURE LSC_SYNPLIFYPRO1 lattice 10.0 11-feb-2024 uncounted \
30EA214E8213 VENDOR_STRING="ispLEVER System with Synplicity \
Pro 1" HOSTID=f81234567890
FEATURE LSC_ADVANCED_DSP lattice 10.0 11-feb-2024 uncounted \
1BFB494C1D0D VENDOR_STRING="ispLEVER DSP" HOSTID=f81234567890
FEATURE LSC_DIAMOND_A lattice 10.0 11-feb-2024 uncounted 1067C09C10BA \
VENDOR_STRING="Diamond Free" HOSTID=f81234567890
FEATURE LSC_PROGRAMMER_MATURE lattice 10.0 11-feb-2024 uncounted \
4559F34D65AB VENDOR_STRING=Programmer HOSTID=f81234567890
FEATURE LSC_ADVANCED_ORCA lattice 9.0 11-feb-2024 uncounted \
CC7BBC2A3A16 VENDOR_STRING="ispORCA System" \
HOSTID=f81234567890
INCREMENT latticemsim mgcld 2023.09 11-feb-2024 0 6FE612FBCA60340B3076 \
VENDOR_STRING=672C91AB HOSTID=f81234567890 ISSUER="ModelSIM Lattice" \
SN=284862648 SIGN2="1E93 D015 CA8E 6012 81DA 3394 F0EA 3C2F 5482 24AB \
F903 2C13 A952 2697 2AF4 0819 2FA9 8CE8 CC9B 329B F71E C11F C29E A733 \
3D18 B2C3 7983 70EA 2885 F23E"

View File

@ -97,9 +97,9 @@
<Property name="PROP_MAP_RegRetiming" value="True" time="0"/> <Property name="PROP_MAP_RegRetiming" value="True" time="0"/>
<Property name="PROP_MAP_SigCrossRef" value="False" time="0"/> <Property name="PROP_MAP_SigCrossRef" value="False" time="0"/>
<Property name="PROP_MAP_SymCrossRef" value="False" time="0"/> <Property name="PROP_MAP_SymCrossRef" value="False" time="0"/>
<Property name="PROP_MAP_TimingDriven" value="False" time="0"/> <Property name="PROP_MAP_TimingDriven" value="True" time="0"/>
<Property name="PROP_MAP_TimingDrivenNodeRep" value="False" time="0"/> <Property name="PROP_MAP_TimingDrivenNodeRep" value="False" time="0"/>
<Property name="PROP_MAP_TimingDrivenPack" value="False" time="0"/> <Property name="PROP_MAP_TimingDrivenPack" value="True" time="0"/>
<Property name="PROP_PARSTA_AnalysisOption" value="Standard Setup and Hold Analysis" time="0"/> <Property name="PROP_PARSTA_AnalysisOption" value="Standard Setup and Hold Analysis" time="0"/>
<Property name="PROP_PARSTA_AutoTiming" value="True" time="0"/> <Property name="PROP_PARSTA_AutoTiming" value="True" time="0"/>
<Property name="PROP_PARSTA_CheckUnconstrainedConns" value="False" time="0"/> <Property name="PROP_PARSTA_CheckUnconstrainedConns" value="False" time="0"/>
@ -118,15 +118,15 @@
<Property name="PROP_PAR_NewRouteParDes" value="NBR" time="0"/> <Property name="PROP_PAR_NewRouteParDes" value="NBR" time="0"/>
<Property name="PROP_PAR_PARClockSkew" value="Off" time="0"/> <Property name="PROP_PAR_PARClockSkew" value="Off" time="0"/>
<Property name="PROP_PAR_PARModArgs" value="" time="0"/> <Property name="PROP_PAR_PARModArgs" value="" time="0"/>
<Property name="PROP_PAR_ParMultiNodeList" value="" time="0"/> <Property name="PROP_PAR_ParMultiNodeList" value="multicore.txt" time="0"/>
<Property name="PROP_PAR_ParRunPlaceOnly" value="False" time="0"/> <Property name="PROP_PAR_ParRunPlaceOnly" value="False" time="0"/>
<Property name="PROP_PAR_PlcIterParDes" value="16" time="0"/> <Property name="PROP_PAR_PlcIterParDes" value="32" time="0"/>
<Property name="PROP_PAR_PlcStCostTblParDes" value="1" time="0"/> <Property name="PROP_PAR_PlcStCostTblParDes" value="1" time="0"/>
<Property name="PROP_PAR_PrefErrorOut" value="True" time="0"/> <Property name="PROP_PAR_PrefErrorOut" value="True" time="0"/>
<Property name="PROP_PAR_RemoveDir" value="True" time="0"/> <Property name="PROP_PAR_RemoveDir" value="True" time="0"/>
<Property name="PROP_PAR_RouteDlyRedParDes" value="0" time="0"/> <Property name="PROP_PAR_RouteDlyRedParDes" value="10" time="0"/>
<Property name="PROP_PAR_RoutePassParDes" value="20" time="0"/> <Property name="PROP_PAR_RoutePassParDes" value="100" time="0"/>
<Property name="PROP_PAR_RouteResOptParDes" value="0" time="0"/> <Property name="PROP_PAR_RouteResOptParDes" value="6" time="0"/>
<Property name="PROP_PAR_RoutingCDP" value="1" time="0"/> <Property name="PROP_PAR_RoutingCDP" value="1" time="0"/>
<Property name="PROP_PAR_RoutingCDR" value="1" time="0"/> <Property name="PROP_PAR_RoutingCDR" value="1" time="0"/>
<Property name="PROP_PAR_RunParWithTrce" value="False" time="0"/> <Property name="PROP_PAR_RunParWithTrce" value="False" time="0"/>
@ -173,7 +173,7 @@
<Property name="PROP_SYN_EdfArrangeVHDLFiles" value="True" time="0"/> <Property name="PROP_SYN_EdfArrangeVHDLFiles" value="True" time="0"/>
<Property name="PROP_SYN_EdfDefEnumEncode" value="Default" time="0"/> <Property name="PROP_SYN_EdfDefEnumEncode" value="Default" time="0"/>
<Property name="PROP_SYN_EdfFanout" value="1000" time="0"/> <Property name="PROP_SYN_EdfFanout" value="1000" time="0"/>
<Property name="PROP_SYN_EdfFrequency" value="110" time="0"/> <Property name="PROP_SYN_EdfFrequency" value="100" time="0"/>
<Property name="PROP_SYN_EdfGSR" value="False" time="0"/> <Property name="PROP_SYN_EdfGSR" value="False" time="0"/>
<Property name="PROP_SYN_EdfInsertIO" value="False" time="0"/> <Property name="PROP_SYN_EdfInsertIO" value="False" time="0"/>
<Property name="PROP_SYN_EdfNumCritPath" value="" time="0"/> <Property name="PROP_SYN_EdfNumCritPath" value="" time="0"/>

View File

@ -3,6 +3,9 @@
<Options/> <Options/>
<Implementation title="impl1" dir="impl1" description="impl1" synthesis="synplify" default_strategy="release"> <Implementation title="impl1" dir="impl1" description="impl1" synthesis="synplify" default_strategy="release">
<Options VerilogStandard="System Verilog" def_top="top" top="top"/> <Options VerilogStandard="System Verilog" def_top="top" top="top"/>
<Source name="../../rtl/memory/dma_scb.sv" type="Verilog" type_short="Verilog">
<Options VerilogStandard="System Verilog"/>
</Source>
<Source name="../../rtl/memory/mem_bus.sv" type="Verilog" type_short="Verilog"> <Source name="../../rtl/memory/mem_bus.sv" type="Verilog" type_short="Verilog">
<Options VerilogStandard="System Verilog"/> <Options VerilogStandard="System Verilog"/>
</Source> </Source>
@ -12,6 +15,9 @@
<Source name="../../rtl/sd/sd_scb.sv" type="Verilog" type_short="Verilog"> <Source name="../../rtl/sd/sd_scb.sv" type="Verilog" type_short="Verilog">
<Options VerilogStandard="System Verilog"/> <Options VerilogStandard="System Verilog"/>
</Source> </Source>
<Source name="../../rtl/usb/usb_scb.sv" type="Verilog" type_short="Verilog">
<Options VerilogStandard="System Verilog"/>
</Source>
<Source name="../../rtl/fifo/fifo_bus.sv" type="Verilog" type_short="Verilog"> <Source name="../../rtl/fifo/fifo_bus.sv" type="Verilog" type_short="Verilog">
<Options VerilogStandard="System Verilog"/> <Options VerilogStandard="System Verilog"/>
</Source> </Source>

View File

@ -96,6 +96,9 @@ IOBUF PORT "sdram_dqm[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 ;
IOBUF PORT "sdram_dqm[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 ; IOBUF PORT "sdram_dqm[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 ;
IOBUF PORT "sdram_ras" PULLMODE=NONE IO_TYPE=LVCMOS33 ; IOBUF PORT "sdram_ras" PULLMODE=NONE IO_TYPE=LVCMOS33 ;
IOBUF PORT "sdram_we" PULLMODE=NONE IO_TYPE=LVCMOS33 ; IOBUF PORT "sdram_we" PULLMODE=NONE IO_TYPE=LVCMOS33 ;
IOBUF PORT "test_point[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 ;
IOBUF PORT "test_point[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 ;
IOBUF PORT "test_point[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 ;
IOBUF PORT "usb_clk" PULLMODE=NONE IO_TYPE=LVCMOS33 ; IOBUF PORT "usb_clk" PULLMODE=NONE IO_TYPE=LVCMOS33 ;
IOBUF PORT "usb_cs" PULLMODE=UP IO_TYPE=LVCMOS33 ; IOBUF PORT "usb_cs" PULLMODE=UP IO_TYPE=LVCMOS33 ;
IOBUF PORT "usb_miosi[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 ; IOBUF PORT "usb_miosi[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 ;
@ -194,6 +197,9 @@ LOCATE COMP "sdram_dqm[0]" SITE "97" ;
LOCATE COMP "sdram_dqm[1]" SITE "73" ; LOCATE COMP "sdram_dqm[1]" SITE "73" ;
LOCATE COMP "sdram_ras" SITE "94" ; LOCATE COMP "sdram_ras" SITE "94" ;
LOCATE COMP "sdram_we" SITE "96" ; LOCATE COMP "sdram_we" SITE "96" ;
LOCATE COMP "test_point[0]" SITE "23" ;
LOCATE COMP "test_point[1]" SITE "24" ;
LOCATE COMP "test_point[2]" SITE "25" ;
LOCATE COMP "usb_clk" SITE "12" ; LOCATE COMP "usb_clk" SITE "12" ;
LOCATE COMP "usb_cs" SITE "11" ; LOCATE COMP "usb_cs" SITE "11" ;
LOCATE COMP "usb_miosi[0]" SITE "22" ; LOCATE COMP "usb_miosi[0]" SITE "22" ;
@ -208,8 +214,48 @@ LOCATE COMP "usb_miso" SITE "10" ;
LOCATE COMP "usb_pwrsav" SITE "2" ; LOCATE COMP "usb_pwrsav" SITE "2" ;
SYSCONFIG SDM_PORT=DISABLE I2C_PORT=ENABLE ; SYSCONFIG SDM_PORT=DISABLE I2C_PORT=ENABLE ;
VOLTAGE 3.300 V; VOLTAGE 3.300 V;
FREQUENCY NET "clk" 100.000000 MHz PAR_ADJ 10.000000 ; FREQUENCY NET "clk" 100.000000 MHz ;
BLOCK PATH TO PORT "mcu_int" ; BLOCK PATH TO PORT "mcu_int" ;
BLOCK PATH TO PORT "n64_irq" ; BLOCK PATH TO PORT "n64_irq" ;
BLOCK PATH FROM PORT "usb_pwrsav" ; BLOCK PATH FROM PORT "usb_pwrsav" ;
BLOCK PATH FROM PORT "sd_det" ; BLOCK PATH FROM PORT "sd_det" ;
DEFINE PORT GROUP "sdram_output" "sdram_cs"
"sdram_ras"
"sdram_cas"
"sdram_we"
"sdram_ba[1]"
"sdram_ba[0]"
"sdram_a[12]"
"sdram_a[11]"
"sdram_a[10]"
"sdram_a[9]"
"sdram_a[8]"
"sdram_a[7]"
"sdram_a[6]"
"sdram_a[5]"
"sdram_a[4]"
"sdram_a[3]"
"sdram_a[2]"
"sdram_a[1]"
"sdram_a[0]"
"sdram_dqm[1]"
"sdram_dqm[0]" ;
DEFINE PORT GROUP "sdram_bidir" "sdram_dq[15]"
"sdram_dq[14]"
"sdram_dq[13]"
"sdram_dq[12]"
"sdram_dq[11]"
"sdram_dq[10]"
"sdram_dq[9]"
"sdram_dq[8]"
"sdram_dq[7]"
"sdram_dq[6]"
"sdram_dq[5]"
"sdram_dq[4]"
"sdram_dq[3]"
"sdram_dq[2]"
"sdram_dq[1]"
"sdram_dq[0]" ;
INPUT_SETUP GROUP "sdram_bidir"INPUT_DELAY 5.400000 ns HOLD -2.500000 ns CLKNET "clk" CLK_OFFSET -0.250000 X ;
CLOCK_TO_OUT GROUP "sdram_output" OUTPUT_DELAY 1.500000 ns MIN 0.800000 ns CLKNET "clk" CLKOUT PORT "sdram_clk" ;
CLOCK_TO_OUT GROUP "sdram_bidir" OUTPUT_DELAY 1.500000 ns MIN 0.800000 ns CLKNET "clk" CLKOUT PORT "sdram_clk" ;

View File

@ -1,35 +1,29 @@
interface fifo_bus (); interface fifo_bus ();
logic rx_empty; logic rx_empty;
logic rx_almost_empty;
logic rx_read; logic rx_read;
logic [7:0] rx_rdata; logic [7:0] rx_rdata;
logic tx_full; logic tx_full;
logic tx_almost_full;
logic tx_write; logic tx_write;
logic [7:0] tx_wdata; logic [7:0] tx_wdata;
modport controller ( modport controller (
input rx_empty, input rx_empty,
input rx_almost_empty,
output rx_read, output rx_read,
input rx_rdata, input rx_rdata,
input tx_full, input tx_full,
input tx_almost_full,
output tx_write, output tx_write,
output tx_wdata output tx_wdata
); );
modport fifo ( modport fifo (
output rx_empty, output rx_empty,
output rx_almost_empty,
input rx_read, input rx_read,
output rx_rdata, output rx_rdata,
output tx_full, output tx_full,
output tx_almost_full,
input tx_write, input tx_write,
input tx_wdata input tx_wdata
); );

View File

@ -11,16 +11,12 @@ module fifo_junction (
dev_bus.tx_wdata = cfg_bus.tx_write ? cfg_bus.tx_wdata : dma_bus.tx_wdata; dev_bus.tx_wdata = cfg_bus.tx_write ? cfg_bus.tx_wdata : dma_bus.tx_wdata;
cfg_bus.rx_empty = dev_bus.rx_empty; cfg_bus.rx_empty = dev_bus.rx_empty;
cfg_bus.rx_almost_empty = dev_bus.rx_almost_empty;
cfg_bus.rx_rdata = dev_bus.rx_rdata; cfg_bus.rx_rdata = dev_bus.rx_rdata;
cfg_bus.tx_full = dev_bus.tx_full; cfg_bus.tx_full = dev_bus.tx_full;
cfg_bus.tx_almost_full = dev_bus.tx_almost_full;
dma_bus.rx_empty = dev_bus.rx_empty; dma_bus.rx_empty = dev_bus.rx_empty;
dma_bus.rx_almost_empty = dev_bus.rx_almost_empty;
dma_bus.rx_rdata = dev_bus.rx_rdata; dma_bus.rx_rdata = dev_bus.rx_rdata;
dma_bus.tx_full = dev_bus.tx_full; dma_bus.tx_full = dev_bus.tx_full;
dma_bus.tx_almost_full = dev_bus.tx_almost_full;
end end
endmodule endmodule

View File

@ -360,7 +360,8 @@ module mcu_top (
REG_DEBUG_0, REG_DEBUG_0,
REG_DEBUG_1, REG_DEBUG_1,
REG_CIC_0, REG_CIC_0,
REG_CIC_1 REG_CIC_1,
REG_AUX
} reg_address_e; } reg_address_e;
logic bootloader_skip; logic bootloader_skip;
@ -370,8 +371,12 @@ module mcu_top (
logic dd_bm_ack; logic dd_bm_ack;
logic [31:0] debug_buffer;
logic cic_invalid_region; logic cic_invalid_region;
logic aux_pending;
// Register read logic // Register read logic
@ -394,13 +399,13 @@ module mcu_top (
REG_USB_SCR: begin REG_USB_SCR: begin
reg_rdata <= { reg_rdata <= {
2'd0, 1'd0,
usb_scb.fifo_flush_busy,
usb_scb.pwrsav, usb_scb.pwrsav,
usb_scb.reset_state, usb_scb.reset_state,
usb_scb.tx_count, usb_scb.tx_count,
usb_scb.rx_count, usb_scb.rx_count,
2'b00, 3'b000,
usb_scb.reset_pending,
~fifo_bus.tx_full, ~fifo_bus.tx_full,
~fifo_bus.rx_empty, ~fifo_bus.rx_empty,
1'b0 1'b0
@ -459,7 +464,9 @@ module mcu_top (
REG_CFG_CMD: begin REG_CFG_CMD: begin
reg_rdata <= { reg_rdata <= {
23'd0, 19'd0,
aux_pending,
3'd0,
n64_scb.cfg_pending, n64_scb.cfg_pending,
n64_scb.cfg_cmd n64_scb.cfg_cmd
}; };
@ -474,7 +481,7 @@ module mcu_top (
18'd0, 18'd0,
n64_scb.flashram_write_or_erase, n64_scb.flashram_write_or_erase,
n64_scb.flashram_sector_or_all, n64_scb.flashram_sector_or_all,
n64_scb.flashram_sector, n64_scb.flashram_page,
n64_scb.flashram_pending, n64_scb.flashram_pending,
1'b0 1'b0
}; };
@ -506,7 +513,7 @@ module mcu_top (
REG_RTC_TIME_1: begin REG_RTC_TIME_1: begin
reg_rdata <= { reg_rdata <= {
8'd0, 7'd0, n64_scb.rtc_rdata[42],
n64_scb.rtc_rdata[41:34], n64_scb.rtc_rdata[41:34],
3'd0, n64_scb.rtc_rdata[33:29], 3'd0, n64_scb.rtc_rdata[33:29],
2'd0, n64_scb.rtc_rdata[25:20] 2'd0, n64_scb.rtc_rdata[25:20]
@ -644,14 +651,18 @@ module mcu_top (
end end
REG_DEBUG_0: begin REG_DEBUG_0: begin
reg_rdata <= n64_scb.pi_debug[31:0]; reg_rdata <= n64_scb.pi_debug_address;
debug_buffer <= {
6'd0,
n64_scb.pi_debug_direction,
n64_scb.pi_debug_rw_count,
n64_scb.cic_debug_step,
n64_scb.pi_debug_fifo_flags
};
end end
REG_DEBUG_1: begin REG_DEBUG_1: begin
reg_rdata <= { reg_rdata <= debug_buffer;
28'd0,
n64_scb.pi_debug[35:32]
};
end end
REG_CIC_0: begin REG_CIC_0: begin
@ -669,6 +680,10 @@ module mcu_top (
REG_CIC_1: begin REG_CIC_1: begin
reg_rdata <= n64_scb.cic_checksum[31:0]; reg_rdata <= n64_scb.cic_checksum[31:0];
end end
REG_AUX: begin
reg_rdata <= n64_scb.aux_rdata;
end
endcase endcase
end end
end end
@ -680,9 +695,10 @@ module mcu_top (
mem_start <= 1'b0; mem_start <= 1'b0;
mem_stop <= 1'b0; mem_stop <= 1'b0;
usb_scb.write_buffer_flush <= 1'b0;
usb_scb.reset_ack <= 1'b0;
usb_scb.fifo_flush <= 1'b0; usb_scb.fifo_flush <= 1'b0;
usb_scb.write_buffer_flush <= 1'b0;
usb_scb.reset_on_ack <= 1'b0;
usb_scb.reset_off_ack <= 1'b0;
usb_dma_scb.start <= 1'b0; usb_dma_scb.start <= 1'b0;
usb_dma_scb.stop <= 1'b0; usb_dma_scb.stop <= 1'b0;
@ -698,7 +714,10 @@ module mcu_top (
n64_scb.cfg_done <= 1'b0; n64_scb.cfg_done <= 1'b0;
n64_scb.cfg_error <= 1'b0; n64_scb.cfg_error <= 1'b0;
n64_scb.cfg_irq <= 1'b0;
n64_scb.btn_irq <= 1'b0;
n64_scb.usb_irq <= 1'b0;
n64_scb.aux_irq <= 1'b0;
n64_scb.flashram_done <= 1'b0; n64_scb.flashram_done <= 1'b0;
@ -729,6 +748,10 @@ module mcu_top (
cic_invalid_region <= 1'b1; cic_invalid_region <= 1'b1;
end end
if (n64_scb.aux_pending) begin
aux_pending <= 1'b1;
end
if (reset) begin if (reset) begin
mcu_int <= 1'b0; mcu_int <= 1'b0;
sd_scb.clock_mode <= 2'd0; sd_scb.clock_mode <= 2'd0;
@ -753,6 +776,7 @@ module mcu_top (
n64_scb.cic_region <= 1'b0; n64_scb.cic_region <= 1'b0;
n64_scb.cic_seed <= 8'h3F; n64_scb.cic_seed <= 8'h3F;
n64_scb.cic_checksum <= 48'hA536C0F1D859; n64_scb.cic_checksum <= 48'hA536C0F1D859;
aux_pending <= 1'b0;
end else if (reg_write) begin end else if (reg_write) begin
case (address) case (address)
REG_MEM_ADDRESS: begin REG_MEM_ADDRESS: begin
@ -769,11 +793,11 @@ module mcu_top (
end end
REG_USB_SCR: begin REG_USB_SCR: begin
{ n64_scb.usb_irq <= reg_wdata[31];
usb_scb.write_buffer_flush, usb_scb.write_buffer_flush <= reg_wdata[5];
usb_scb.reset_ack, usb_scb.reset_off_ack <= reg_wdata[4];
usb_scb.fifo_flush usb_scb.reset_on_ack <= reg_wdata[3];
} <= {reg_wdata[5:4], reg_wdata[0]}; usb_scb.fifo_flush <= reg_wdata[0];
end end
REG_USB_DMA_ADDRESS: begin REG_USB_DMA_ADDRESS: begin
@ -819,10 +843,13 @@ module mcu_top (
REG_CFG_CMD: begin REG_CFG_CMD: begin
{ {
n64_scb.cfg_irq, n64_scb.btn_irq,
n64_scb.cfg_error, n64_scb.cfg_error,
n64_scb.cfg_done n64_scb.cfg_done
} <= reg_wdata[11:9]; } <= reg_wdata[11:9];
if (reg_wdata[13]) begin
aux_pending <= 1'b0;
end
end end
REG_FLASHRAM_SCR: begin REG_FLASHRAM_SCR: begin
@ -848,6 +875,7 @@ module mcu_top (
REG_RTC_TIME_1: begin REG_RTC_TIME_1: begin
n64_scb.rtc_wdata_valid <= 1'b1; n64_scb.rtc_wdata_valid <= 1'b1;
n64_scb.rtc_wdata[42] <= reg_wdata[24];
n64_scb.rtc_wdata[41:34] <= reg_wdata[23:16]; n64_scb.rtc_wdata[41:34] <= reg_wdata[23:16];
n64_scb.rtc_wdata[33:29] <= reg_wdata[12:8]; n64_scb.rtc_wdata[33:29] <= reg_wdata[12:8];
n64_scb.rtc_wdata[25:20] <= reg_wdata[5:0]; n64_scb.rtc_wdata[25:20] <= reg_wdata[5:0];
@ -945,6 +973,11 @@ module mcu_top (
REG_CIC_1: begin REG_CIC_1: begin
n64_scb.cic_checksum[31:0] <= reg_wdata; n64_scb.cic_checksum[31:0] <= reg_wdata;
end end
REG_AUX: begin
n64_scb.aux_irq <= 1'b1;
n64_scb.aux_wdata <= reg_wdata;
end
endcase endcase
end end
end end

31
fw/rtl/memory/dma_scb.sv Normal file
View File

@ -0,0 +1,31 @@
interface dma_scb ();
logic start;
logic stop;
logic busy;
logic direction;
logic byte_swap;
logic [26:0] starting_address;
logic [26:0] transfer_length;
modport controller (
output start,
output stop,
input busy,
output direction,
output byte_swap,
output starting_address,
output transfer_length
);
modport dma (
input start,
input stop,
output busy,
input direction,
input byte_swap,
input starting_address,
input transfer_length
);
endinterface

View File

@ -36,11 +36,11 @@ module memory_bram (
eeprom_selected = 1'b0; eeprom_selected = 1'b0;
dd_selected = 1'b0; dd_selected = 1'b0;
flashram_selected = 1'b0; flashram_selected = 1'b0;
if (mem_bus.address[25:24] == 2'b01 && mem_bus.address[23:14] == 10'd0) begin if (mem_bus.address[26:24] == 3'h5) begin
buffer_selected = mem_bus.address[13] == 1'b0; buffer_selected = (mem_bus.address[23:0] >= 24'h00_0000 && mem_bus.address[23:0] < 24'h00_2000);
eeprom_selected = mem_bus.address[13:11] == 3'b100; eeprom_selected = (mem_bus.address[23:0] >= 24'h00_2000 && mem_bus.address[23:0] < 24'h00_2800);
dd_selected = mem_bus.address[13:8] == 6'b101000; dd_selected = (mem_bus.address[23:0] >= 24'h00_2800 && mem_bus.address[23:0] < 24'h00_2C00);
flashram_selected = mem_bus.address[13:7] == 7'b1010010; flashram_selected = (mem_bus.address[23:0] >= 24'h00_2C00 && mem_bus.address[23:0] < 24'h00_2C80);
end end
end end
@ -112,26 +112,26 @@ module memory_bram (
end end
// DD memory // 64DD/MCU buffer memory
logic [15:0] dd_bram [0:127]; logic [15:0] dd_bram [0:511];
logic [15:0] dd_bram_rdata; logic [15:0] dd_bram_rdata;
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
if (write && dd_selected) begin if (write && dd_selected) begin
dd_bram[mem_bus.address[7:1]] <= mem_bus.wdata; dd_bram[mem_bus.address[9:1]] <= mem_bus.wdata;
end end
if (n64_scb.dd_write) begin if (n64_scb.dd_write) begin
dd_bram[n64_scb.dd_address] <= n64_scb.dd_wdata; dd_bram[{2'b00, n64_scb.dd_address}] <= n64_scb.dd_wdata;
end end
end end
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
dd_bram_rdata <= dd_bram[mem_bus.address[7:1]]; dd_bram_rdata <= dd_bram[mem_bus.address[9:1]];
end end
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
n64_scb.dd_rdata <= dd_bram[n64_scb.dd_address]; n64_scb.dd_rdata <= dd_bram[{2'b00, n64_scb.dd_address}];
end end

View File

@ -1,36 +1,3 @@
interface dma_scb ();
logic start;
logic stop;
logic busy;
logic direction;
logic byte_swap;
logic [26:0] starting_address;
logic [26:0] transfer_length;
modport controller (
output start,
output stop,
input busy,
output direction,
output byte_swap,
output starting_address,
output transfer_length
);
modport dma (
input start,
input stop,
output busy,
input direction,
input byte_swap,
input starting_address,
input transfer_length
);
endinterface
module memory_dma ( module memory_dma (
input clk, input clk,
input reset, input reset,
@ -41,213 +8,382 @@ module memory_dma (
mem_bus.controller mem_bus mem_bus.controller mem_bus
); );
// DMA start/stop control // Start/stop logic
logic dma_start; logic dma_start;
logic dma_stop; logic dma_stop;
always_comb begin always_ff @(posedge clk) begin
dma_start = dma_scb.start && !dma_scb.stop && !dma_scb.busy; dma_start <= 1'b0;
dma_stop = dma_scb.stop; if (dma_scb.stop) begin
dma_stop <= 1'b1;
end else if (dma_scb.start) begin
dma_start <= 1'b1;
dma_stop <= 1'b0;
end
end end
// Remaining counter and FIFO enable // Memory bus controller
logic [26:0] remaining; typedef enum bit [1:0] {
logic trx_enabled; MEM_BUS_STATE_IDLE,
MEM_BUS_STATE_WAIT,
MEM_BUS_STATE_TRANSFER
} e_mem_bus_state;
e_mem_bus_state mem_bus_state;
e_mem_bus_state next_mem_bus_state;
logic mem_bus_wdata_ready;
logic mem_bus_wdata_empty;
logic mem_bus_wdata_end;
logic [1:0] mem_bus_wdata_valid;
logic [15:0] mem_bus_wdata_buffer;
logic mem_bus_rdata_ready;
logic mem_bus_rdata_ack;
logic mem_bus_rdata_end;
logic [1:0] mem_bus_rdata_valid;
logic [15:0] mem_bus_rdata_buffer;
logic [26:0] mem_bus_remaining_bytes;
logic mem_bus_last_transfer;
logic mem_bus_almost_last_transfer;
logic mem_bus_unaligned_start;
logic mem_bus_unaligned_end;
always_ff @(posedge clk) begin
if (reset) begin
mem_bus_state <= MEM_BUS_STATE_IDLE;
end else begin
mem_bus_state <= next_mem_bus_state;
end
end
always_comb begin always_comb begin
trx_enabled = remaining > 27'd0; next_mem_bus_state = mem_bus_state;
mem_bus_last_transfer = (mem_bus_remaining_bytes == 27'd0);
mem_bus_almost_last_transfer = (mem_bus_remaining_bytes <= 27'd2);
mem_bus_wdata_end = mem_bus_last_transfer;
mem_bus_wdata_valid = (mem_bus_unaligned_end && mem_bus_almost_last_transfer) ? 2'b10 :
mem_bus_unaligned_start ? 2'b01 :
2'b11;
case (mem_bus_state)
MEM_BUS_STATE_IDLE: begin
if (dma_start) begin
next_mem_bus_state = MEM_BUS_STATE_WAIT;
end
end
MEM_BUS_STATE_WAIT: begin
if (dma_stop || mem_bus_last_transfer) begin
next_mem_bus_state = MEM_BUS_STATE_IDLE;
end else if (mem_bus_wdata_ready || !mem_bus_wdata_empty || mem_bus_rdata_ready) begin
next_mem_bus_state = MEM_BUS_STATE_TRANSFER;
end
end
MEM_BUS_STATE_TRANSFER: begin
if (mem_bus.ack) begin
if (dma_stop || mem_bus_last_transfer) begin
next_mem_bus_state = MEM_BUS_STATE_IDLE;
end else if (!(mem_bus_wdata_ready || !mem_bus_wdata_empty || mem_bus_rdata_ready)) begin
next_mem_bus_state = MEM_BUS_STATE_WAIT;
end
end
end
default: begin
next_mem_bus_state = MEM_BUS_STATE_IDLE;
end
endcase
end
always_ff @(posedge clk) begin
mem_bus_rdata_ack <= 1'b0;
if (mem_bus.ack) begin
mem_bus.request <= 1'b0;
mem_bus.address <= (mem_bus.address + 26'd2);
mem_bus_rdata_ack <= 1'b1;
mem_bus_rdata_end <= mem_bus_last_transfer;
mem_bus_rdata_valid <= mem_bus.wmask;
mem_bus_rdata_buffer <= mem_bus.rdata;
end
if (mem_bus_wdata_ready) begin
mem_bus_wdata_empty <= 1'b0;
end
case (mem_bus_state)
MEM_BUS_STATE_IDLE: begin
mem_bus.request <= 1'b0;
mem_bus_rdata_end <= 1'b1;
if (dma_start) begin
mem_bus.write <= dma_scb.direction;
mem_bus.address <= {dma_scb.starting_address[26:1], 1'b0};
mem_bus_remaining_bytes <= dma_scb.transfer_length;
mem_bus_unaligned_start <= dma_scb.starting_address[0];
mem_bus_unaligned_end <= (dma_scb.starting_address[0] ^ dma_scb.transfer_length[0]);
mem_bus_rdata_end <= 1'b0;
mem_bus_wdata_empty <= 1'b1;
end
end
MEM_BUS_STATE_WAIT: begin
if (!dma_stop && !mem_bus_last_transfer) begin
if (mem_bus_wdata_ready || !mem_bus_wdata_empty || mem_bus_rdata_ready) begin
mem_bus.request <= 1'b1;
mem_bus_unaligned_start <= 1'b0;
mem_bus.wdata <= (dma_scb.byte_swap ? {mem_bus_wdata_buffer[7:0], mem_bus_wdata_buffer[15:8]} : mem_bus_wdata_buffer);
mem_bus.wmask <= 2'b11;
mem_bus_remaining_bytes <= (mem_bus_remaining_bytes - 27'd2);
if (mem_bus_unaligned_end && mem_bus_almost_last_transfer) begin
mem_bus.wmask <= 2'b10;
mem_bus_remaining_bytes <= (mem_bus_remaining_bytes - 27'd1);
end
if (mem_bus_unaligned_start) begin
mem_bus.wmask <= 2'b01;
mem_bus_remaining_bytes <= (mem_bus_remaining_bytes - 27'd1);
end
mem_bus_wdata_empty <= 1'b1;
end
end
end
MEM_BUS_STATE_TRANSFER: begin
if (!dma_stop && !mem_bus_last_transfer) begin
if (mem_bus.ack && (mem_bus_wdata_ready || !mem_bus_wdata_empty || mem_bus_rdata_ready)) begin
mem_bus.request <= 1'b1;
mem_bus.wdata <= (dma_scb.byte_swap ? {mem_bus_wdata_buffer[7:0], mem_bus_wdata_buffer[15:8]} : mem_bus_wdata_buffer);
mem_bus.wmask <= 2'b11;
mem_bus_remaining_bytes <= (mem_bus_remaining_bytes - 27'd2);
if (mem_bus_unaligned_end && mem_bus_almost_last_transfer) begin
mem_bus.wmask <= 2'b10;
mem_bus_remaining_bytes <= (mem_bus_remaining_bytes - 27'd1);
end
mem_bus_wdata_empty <= 1'b1;
end
end
end
default: begin end
endcase
end end
// RX FIFO controller // RX FIFO controller
logic [1:0] rx_wmask; typedef enum bit [1:0] {
logic rx_rdata_pop; RX_FIFO_BUS_STATE_IDLE,
logic rx_rdata_shift; RX_FIFO_BUS_STATE_TRANSFER_1,
logic rx_rdata_valid; RX_FIFO_BUS_STATE_TRANSFER_2,
logic [15:0] rx_buffer; RX_FIFO_BUS_STATE_ACK
logic rx_buffer_valid; } e_rx_fifo_bus_state;
logic [1:0] rx_buffer_counter;
logic [1:0] rx_buffer_valid_counter; e_rx_fifo_bus_state rx_fifo_bus_state;
e_rx_fifo_bus_state next_rx_fifo_bus_state;
logic rx_fifo_shift;
logic rx_fifo_shift_delayed;
always_ff @(posedge clk) begin
if (reset || dma_stop) begin
rx_fifo_bus_state <= RX_FIFO_BUS_STATE_IDLE;
end else begin
rx_fifo_bus_state <= next_rx_fifo_bus_state;
end
end
always_comb begin always_comb begin
rx_buffer_valid = rx_buffer_valid_counter == 2'd2; next_rx_fifo_bus_state = rx_fifo_bus_state;
rx_fifo_shift = 1'b0;
fifo_bus.rx_read = 1'b0;
case (rx_fifo_bus_state)
RX_FIFO_BUS_STATE_IDLE: begin
if (dma_start && dma_scb.direction) begin
next_rx_fifo_bus_state = RX_FIFO_BUS_STATE_TRANSFER_1;
end
end
RX_FIFO_BUS_STATE_TRANSFER_1: begin
fifo_bus.rx_read = (!fifo_bus.rx_empty && mem_bus_wdata_empty && mem_bus_wdata_valid[1] && !mem_bus_wdata_end);
if ((!fifo_bus.rx_empty && mem_bus_wdata_empty) || !mem_bus_wdata_valid[1]) begin
next_rx_fifo_bus_state = RX_FIFO_BUS_STATE_TRANSFER_2;
rx_fifo_shift = 1'b1;
end
if (mem_bus_wdata_end) begin
next_rx_fifo_bus_state = RX_FIFO_BUS_STATE_IDLE;
end
end
RX_FIFO_BUS_STATE_TRANSFER_2: begin
fifo_bus.rx_read = (!fifo_bus.rx_empty && mem_bus_wdata_valid[0]);
if (!fifo_bus.rx_empty || !mem_bus_wdata_valid[0]) begin
next_rx_fifo_bus_state = RX_FIFO_BUS_STATE_ACK;
rx_fifo_shift = 1'b1;
end
end
RX_FIFO_BUS_STATE_ACK: begin
if (mem_bus_wdata_ready) begin
next_rx_fifo_bus_state = RX_FIFO_BUS_STATE_TRANSFER_1;
end
end
default: begin
next_rx_fifo_bus_state = RX_FIFO_BUS_STATE_IDLE;
end
endcase
end end
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
rx_rdata_pop <= ( mem_bus_wdata_ready <= 1'b0;
!rx_rdata_pop && rx_fifo_shift_delayed <= rx_fifo_shift;
!fifo_bus.rx_read &&
trx_enabled &&
rx_buffer_counter < 2'd2 &&
!fifo_bus.rx_empty &&
mem_bus.write
);
rx_rdata_shift <= 1'b0;
fifo_bus.rx_read <= rx_rdata_pop;
rx_rdata_valid <= fifo_bus.rx_read;
if (dma_start) begin if (rx_fifo_shift_delayed) begin
if (dma_scb.starting_address[0]) begin if (rx_fifo_bus_state == RX_FIFO_BUS_STATE_ACK) begin
rx_wmask <= 2'b01; mem_bus_wdata_ready <= 1'b1;
rx_buffer_counter <= 2'd1;
rx_buffer_valid_counter <= 2'd1;
end else begin
rx_wmask <= 2'b11;
rx_buffer_counter <= 2'd0;
rx_buffer_valid_counter <= 2'd0;
end end
end mem_bus_wdata_buffer <= {mem_bus_wdata_buffer[7:0], fifo_bus.rx_rdata};
if (rx_rdata_pop) begin
rx_buffer_counter <= rx_buffer_counter + 1'd1;
end
if (rx_rdata_shift || rx_rdata_valid) begin
if (dma_scb.byte_swap) begin
rx_buffer <= {fifo_bus.rx_rdata, rx_buffer[15:8]};
end else begin
rx_buffer <= {rx_buffer[7:0], fifo_bus.rx_rdata};
end
rx_buffer_valid_counter <= rx_buffer_valid_counter + 1'd1;
if (remaining == 27'd0 && rx_buffer_counter == 2'd1) begin
rx_wmask <= 2'b10;
rx_rdata_shift <= 1'b1;
rx_buffer_counter <= rx_buffer_counter + 1'd1;
end
end
if (rx_buffer_valid && !mem_bus.request) begin
rx_wmask <= 2'b11;
rx_buffer_counter <= 2'd0;
rx_buffer_valid_counter <= 2'd0;
end end
end end
// TX FIFO controller // TX FIFO controller
logic tx_wdata_push; typedef enum bit [1:0] {
logic tx_wdata_first_push; TX_FIFO_BUS_STATE_IDLE,
logic [7:0] tx_buffer; TX_FIFO_BUS_STATE_WAIT,
logic tx_buffer_counter; TX_FIFO_BUS_STATE_TRANSFER_1,
logic tx_buffer_ready; TX_FIFO_BUS_STATE_TRANSFER_2
logic tx_buffer_valid; } e_tx_fifo_bus_state;
e_tx_fifo_bus_state tx_fifo_bus_state;
e_tx_fifo_bus_state next_tx_fifo_bus_state;
logic tx_fifo_shift;
logic tx_fifo_waiting;
logic [1:0] tx_fifo_valid;
logic [15:0] tx_fifo_buffer;
always_ff @(posedge clk) begin
if (reset || dma_stop) begin
tx_fifo_bus_state <= TX_FIFO_BUS_STATE_IDLE;
end else begin
tx_fifo_bus_state <= next_tx_fifo_bus_state;
end
end
always_comb begin always_comb begin
fifo_bus.tx_write = tx_wdata_push; next_tx_fifo_bus_state = tx_fifo_bus_state;
end
always_ff @(posedge clk) begin tx_fifo_shift = 1'b0;
tx_wdata_push <= (
!tx_wdata_push &&
trx_enabled &&
tx_buffer_valid &&
!fifo_bus.tx_full &&
!mem_bus.write
);
if (reset || dma_stop) begin fifo_bus.tx_write = 1'b0;
tx_buffer_ready <= 1'b0; fifo_bus.tx_wdata = tx_fifo_buffer[15:8];
tx_buffer_valid <= 1'b0;
end
if (dma_start) begin case (tx_fifo_bus_state)
tx_wdata_first_push <= 1'b1; TX_FIFO_BUS_STATE_IDLE: begin
tx_buffer_ready <= 1'b1; if (dma_start && !dma_scb.direction) begin
tx_buffer_valid <= 1'b0; next_tx_fifo_bus_state = TX_FIFO_BUS_STATE_WAIT;
end end
if (tx_buffer_ready && mem_bus.request) begin
tx_buffer_ready <= 1'b0;
end
if (mem_bus.ack) begin
tx_wdata_first_push <= 1'b0;
tx_buffer_counter <= 1'd1;
tx_buffer_valid <= 1'b1;
{fifo_bus.tx_wdata, tx_buffer} <= mem_bus.rdata;
if (tx_wdata_first_push && dma_scb.starting_address[0]) begin
fifo_bus.tx_wdata <= mem_bus.rdata[7:0];
tx_buffer_counter <= 1'd0;
end
end
if (tx_wdata_push) begin
tx_buffer_counter <= tx_buffer_counter - 1'd1;
fifo_bus.tx_wdata <= tx_buffer;
if (tx_buffer_counter == 1'd0) begin
tx_buffer_ready <= 1'b1;
tx_buffer_valid <= 1'b0;
end
end
end
// Remaining counter controller
always_ff @(posedge clk) begin
if (reset || dma_stop) begin
remaining <= 27'd0;
end else begin
if (dma_start) begin
remaining <= dma_scb.transfer_length;
end end
if ((mem_bus.write && rx_rdata_pop) || (!mem_bus.write && tx_wdata_push)) begin TX_FIFO_BUS_STATE_WAIT: begin
remaining <= remaining - 1'd1; if (mem_bus_rdata_ack || tx_fifo_waiting) begin
next_tx_fifo_bus_state = TX_FIFO_BUS_STATE_TRANSFER_1;
end else if (mem_bus_rdata_end) begin
next_tx_fifo_bus_state = TX_FIFO_BUS_STATE_IDLE;
end
end end
end
end
TX_FIFO_BUS_STATE_TRANSFER_1: begin
fifo_bus.tx_write = (!fifo_bus.tx_full && tx_fifo_valid[1]);
if (!fifo_bus.tx_full || !tx_fifo_valid[1]) begin
next_tx_fifo_bus_state = TX_FIFO_BUS_STATE_TRANSFER_2;
tx_fifo_shift = 1'b1;
end
end
// Mem bus controller TX_FIFO_BUS_STATE_TRANSFER_2: begin
fifo_bus.tx_write = (!fifo_bus.tx_full && tx_fifo_valid[1]);
always_ff @(posedge clk) begin if (!fifo_bus.tx_full || !tx_fifo_valid[1]) begin
dma_scb.busy <= mem_bus.request || trx_enabled; next_tx_fifo_bus_state = TX_FIFO_BUS_STATE_WAIT;
end tx_fifo_shift = 1'b1;
if (!mem_bus_rdata_ack && !tx_fifo_waiting && mem_bus_rdata_end) begin
always_ff @(posedge clk) begin next_tx_fifo_bus_state = TX_FIFO_BUS_STATE_IDLE;
if (reset) begin
mem_bus.request <= 1'b0;
end else begin
if (!mem_bus.request) begin
if (mem_bus.write) begin
if (rx_buffer_valid) begin
mem_bus.request <= 1'b1;
mem_bus.wmask <= rx_wmask;
mem_bus.wdata <= rx_buffer;
end
end else begin
if (tx_buffer_ready) begin
mem_bus.request <= 1'b1;
end end
end end
end end
end
if (mem_bus.ack) begin default: begin
mem_bus.request <= 1'b0; next_tx_fifo_bus_state = TX_FIFO_BUS_STATE_IDLE;
end end
endcase
end end
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
if (dma_start) begin if (tx_fifo_shift) begin
mem_bus.write <= dma_scb.direction; tx_fifo_valid <= {tx_fifo_valid[0], 1'bX};
tx_fifo_buffer <= {tx_fifo_buffer[7:0], 8'hXX};
end end
case (tx_fifo_bus_state)
TX_FIFO_BUS_STATE_IDLE: begin
mem_bus_rdata_ready <= 1'b0;
tx_fifo_waiting <= 1'b0;
if (dma_start) begin
mem_bus_rdata_ready <= !dma_scb.direction;
end
end
TX_FIFO_BUS_STATE_WAIT: begin
if (mem_bus_rdata_ack || tx_fifo_waiting) begin
mem_bus_rdata_ready <= 1'b0;
tx_fifo_waiting <= 1'b0;
tx_fifo_valid <= mem_bus_rdata_valid;
tx_fifo_buffer <= mem_bus_rdata_buffer;
end
end
TX_FIFO_BUS_STATE_TRANSFER_1: begin
if (mem_bus_rdata_ack) begin
tx_fifo_waiting <= 1'b1;
end
end
TX_FIFO_BUS_STATE_TRANSFER_2: begin
if (mem_bus_rdata_ack) begin
tx_fifo_waiting <= 1'b1;
end
if (fifo_bus.tx_write || !tx_fifo_valid[1]) begin
mem_bus_rdata_ready <= !mem_bus_rdata_end;
end
end
default: begin end
endcase
end end
always_ff @(posedge clk) begin
if (dma_start) begin
mem_bus.address <= {dma_scb.starting_address[26:1], 1'b0};
end
if (mem_bus.ack) begin // DMA busy indicator
mem_bus.address <= mem_bus.address + 2'd2;
end always_ff @(posedge clk) begin
dma_scb.busy <= (
(dma_scb.start && !dma_scb.stop) ||
dma_start ||
(mem_bus_state != MEM_BUS_STATE_IDLE) ||
(rx_fifo_bus_state != RX_FIFO_BUS_STATE_IDLE) ||
(tx_fifo_bus_state != TX_FIFO_BUS_STATE_IDLE)
);
end end
endmodule endmodule

View File

@ -234,7 +234,7 @@ module memory_flash (
if (reset) begin if (reset) begin
state <= STATE_IDLE; state <= STATE_IDLE;
end else begin end else begin
if (!busy && (start || finish)) begin if ((start || finish) && !busy) begin
counter <= counter + 1'd1; counter <= counter + 1'd1;
end end
@ -248,6 +248,7 @@ module memory_flash (
end else if (mem_bus.request) begin end else if (mem_bus.request) begin
current_address <= {mem_bus.address[23:1], 1'b0}; current_address <= {mem_bus.address[23:1], 1'b0};
if (mem_bus.write) begin if (mem_bus.write) begin
current_address[0] <= (~mem_bus.wmask[1]);
state <= STATE_WRITE_ENABLE; state <= STATE_WRITE_ENABLE;
end else begin end else begin
state <= STATE_READ_START; state <= STATE_READ_START;
@ -263,7 +264,7 @@ module memory_flash (
end end
3'd1: begin 3'd1: begin
finish <= 1'b1; finish <= 1'b1;
wdata <= 8'd4; wdata <= 8'd5;
if (!busy) begin if (!busy) begin
counter <= 3'd0; counter <= 3'd0;
if (flash_scb.erase_pending) begin if (flash_scb.erase_pending) begin
@ -296,7 +297,7 @@ module memory_flash (
end end
3'd4: begin 3'd4: begin
finish <= 1'b1; finish <= 1'b1;
wdata <= 8'd4; wdata <= 8'd5;
if (!busy) begin if (!busy) begin
flash_scb.erase_done <= 1'b1; flash_scb.erase_done <= 1'b1;
counter <= 3'd0; counter <= 3'd0;
@ -314,17 +315,17 @@ module memory_flash (
end end
3'd1: begin 3'd1: begin
start <= 1'b1; start <= 1'b1;
wdata <= mem_bus.address[23:16]; wdata <= current_address[23:16];
end end
3'd2: begin 3'd2: begin
start <= 1'b1; start <= 1'b1;
wdata <= mem_bus.address[15:8]; wdata <= current_address[15:8];
end end
3'd3: begin 3'd3: begin
start <= 1'b1; start <= 1'b1;
wdata <= mem_bus.address[7:0]; wdata <= current_address[7:0];
if (!busy) begin if (!busy) begin
counter <= 3'd0; counter <= 3'd0 + current_address[0];
state <= STATE_PROGRAM; state <= STATE_PROGRAM;
end end
end end
@ -336,26 +337,33 @@ module memory_flash (
3'd0: begin 3'd0: begin
start <= 1'b1; start <= 1'b1;
wdata <= mem_bus.wdata[15:8]; wdata <= mem_bus.wdata[15:8];
if (start && !busy) begin
current_address <= current_address + 1'd1;
if (!mem_bus.wmask[0]) begin
counter <= 3'd2;
mem_bus.ack <= 1'b1;
end
end
end end
3'd1: begin 3'd1: begin
start <= 1'b1; start <= 1'b1;
wdata <= mem_bus.wdata[7:0]; wdata <= mem_bus.wdata[7:0];
if (!busy) begin if (!busy) begin
mem_bus.ack <= 1'b1; mem_bus.ack <= 1'b1;
current_address <= current_address + 2'd2; current_address <= current_address + 1'd1;
end end
end end
3'd2: begin 3'd2: begin
if (current_address[7:0] == 8'h00) begin if (current_address[7:0] == 8'h00) begin
state <= STATE_PROGRAM_END; state <= STATE_PROGRAM_END;
end else if (flash_scb.erase_pending) begin
state <= STATE_PROGRAM_END;
end else if (mem_bus.request && !mem_bus.ack) begin end else if (mem_bus.request && !mem_bus.ack) begin
if (!mem_bus.write || (mem_bus.address[23:0] != current_address)) begin if (mem_bus.write && mem_bus.wmask[1] && (mem_bus.address[23:0] == current_address)) begin
state <= STATE_PROGRAM_END;
end else begin
counter <= 3'd0; counter <= 3'd0;
end else begin
state <= STATE_PROGRAM_END;
end end
end else if (!busy) begin
state <= STATE_PROGRAM_END;
end end
end end
endcase endcase
@ -363,8 +371,8 @@ module memory_flash (
STATE_PROGRAM_END: begin STATE_PROGRAM_END: begin
finish <= 1'b1; finish <= 1'b1;
wdata <= 8'd4; wdata <= 8'd5;
if (!busy) begin if (finish && !busy) begin
counter <= 3'd0; counter <= 3'd0;
state <= STATE_WAIT; state <= STATE_WAIT;
end end
@ -407,15 +415,15 @@ module memory_flash (
3'd1: begin 3'd1: begin
start <= 1'b1; start <= 1'b1;
quad_enable <= 1'b1; quad_enable <= 1'b1;
wdata <= mem_bus.address[23:16]; wdata <= current_address[23:16];
end end
3'd2: begin 3'd2: begin
start <= 1'b1; start <= 1'b1;
wdata <= mem_bus.address[15:8]; wdata <= current_address[15:8];
end end
3'd3: begin 3'd3: begin
start <= 1'b1; start <= 1'b1;
wdata <= mem_bus.address[7:0]; wdata <= current_address[7:0];
end end
3'd4: begin 3'd4: begin
start <= 1'b1; start <= 1'b1;

View File

@ -14,29 +14,38 @@ module memory_sdram (
inout [15:0] sdram_dq inout [15:0] sdram_dq
); );
localparam [2:0] CAS_LATENCY = 3'd2; // in Hz
localparam real FREQUENCY = 100_000_000.0;
localparam real T_INIT = 100_000.0; // in clocks
localparam real T_RC = 60.0; localparam bit [2:0] CAS_LATENCY = 3'd2;
localparam real T_RP = 15.0;
localparam real T_RCD = 15.0;
localparam real T_MRD = 14.0;
localparam real T_REF = 7_800.0;
localparam real T_CLK = (1.0 / 100_000_000) * 1_000_000_000.0; // in nanoseconds
localparam int C_INIT = int'((T_INIT + T_CLK - 1) / T_CLK); localparam real T_INIT = 100_000.0;
localparam int C_RC = int'((T_RC + T_CLK - 1) / T_CLK); localparam real T_MRD = 14.0;
localparam int C_RP = int'((T_RP + T_CLK - 1) / T_CLK); localparam real T_RAS = 37.0;
localparam int C_RCD = int'((T_RCD + T_CLK - 1) / T_CLK); localparam real T_RC = 60.0;
localparam int C_MRD = int'((T_MRD + T_CLK - 1) / T_CLK); localparam real T_RCD = 15.0;
localparam int C_REF = int'((T_REF + T_CLK - 1) / T_CLK); localparam real T_REF = 7_812.5;
localparam real T_RP = 15.0;
localparam INIT_PRECHARGE = 4'd0; localparam real T_CLK = (1.0 / FREQUENCY) * 1_000_000_000.0;
localparam INIT_REFRESH_1 = C_RP;
localparam INIT_REFRESH_2 = C_RP + C_RC;
localparam INIT_MODE_REG = C_RP + (2 * C_RC);
localparam INIT_DONE = C_RP + (2 * C_RC) + C_MRD;
const bit [13:0] C_INIT = 14'(int'($ceil(T_INIT / T_CLK)));
const bit [4:0] C_MRD = 5'(int'($ceil(T_MRD / T_CLK)));
const bit [2:0] C_RAS = 3'(int'($ceil(T_RAS / T_CLK)));
const bit [4:0] C_RC = 5'(int'($ceil(T_RC / T_CLK)));
const bit [4:0] C_RCD = 5'(int'($ceil(T_RCD / T_CLK)));
const bit [13:0] C_REF = 14'(int'($ceil(T_REF / T_CLK)));
const bit [4:0] C_RP = 5'(int'($ceil(T_RP / T_CLK)));
const bit [4:0] INIT_PRECHARGE = 5'd0;
const bit [4:0] INIT_REFRESH_1 = INIT_PRECHARGE + C_RP;
const bit [4:0] INIT_REFRESH_2 = INIT_REFRESH_1 + C_RC;
const bit [4:0] INIT_MODE_REG = INIT_REFRESH_2 + C_RC;
const bit [4:0] INIT_DONE = INIT_MODE_REG + C_MRD;
// /CS, /RAS, /CAS, /WE
typedef enum bit [3:0] { typedef enum bit [3:0] {
CMD_DESL = 4'b1111, CMD_DESL = 4'b1111,
CMD_NOP = 4'b0111, CMD_NOP = 4'b0111,
@ -58,13 +67,10 @@ module memory_sdram (
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
{sdram_cs, sdram_ras, sdram_cas, sdram_we} <= 4'(sdram_next_cmd); {sdram_cs, sdram_ras, sdram_cas, sdram_we} <= 4'(sdram_next_cmd);
{sdram_ba, sdram_a} <= 15'd0; {sdram_ba, sdram_a} <= 15'd0;
sdram_dqm <= 2'b00; sdram_dqm <= 2'b00;
sdram_dq_input <= sdram_dq; sdram_dq_input <= sdram_dq;
sdram_dq_output <= mem_bus.wdata; sdram_dq_output <= mem_bus.wdata;
sdram_dq_output_enable <= 1'b0; sdram_dq_output_enable <= 1'b0;
case (sdram_next_cmd) case (sdram_next_cmd)
@ -76,19 +82,31 @@ module memory_sdram (
CMD_ACT: begin CMD_ACT: begin
{sdram_ba, sdram_a} <= mem_bus.address[25:11]; {sdram_ba, sdram_a} <= mem_bus.address[25:11];
sdram_dqm <= 2'b00;
current_active_bank_row <= mem_bus.address[25:11]; current_active_bank_row <= mem_bus.address[25:11];
end end
CMD_PRE: begin CMD_PRE: begin
{sdram_ba, sdram_a} <= {2'b00, 2'b00, 1'b1, 10'd0}; {sdram_ba, sdram_a} <= {
sdram_dqm <= 2'b00; 2'b00, // [BA1:BA0] Don't care
2'b00, // [A12:A11] Don't care
1'b1, // [A10] Precharge all banks
10'd0 // [A9:A0] Don't care
};
end end
CMD_MRS: begin CMD_MRS: begin
{sdram_ba, sdram_a} <= {2'b00, 1'b0, 1'b0, 2'b00, CAS_LATENCY, 1'b0, 3'b000}; {sdram_ba, sdram_a} <= {
sdram_dqm <= 2'b00; 2'b00, // [BA1:BA0] Reserved = 0
3'b000, // [A12:A10] Reserved = 0
1'b0, // [A9] Write Burst Mode = Programmed Burst Length
2'b00, // [A8:A7] Operating Mode = Standard Operation
CAS_LATENCY, // [A6:A4] Latency Mode = 2
1'b0, // [A3] Burst Type = Sequential
3'b000 // [A2:A0] Burst Length = 1
};
end end
default: begin end
endcase endcase
end end
@ -121,35 +139,51 @@ module memory_sdram (
end end
end end
logic [13:0] powerup_coutner; logic [13:0] refresh_counter;
logic powerup_done;
logic [4:0] wait_counter; logic [4:0] wait_counter;
logic [9:0] refresh_counter; logic [2:0] precharge_counter;
logic powerup_done;
logic pending_refresh; logic pending_refresh;
logic precharge_valid;
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
if (reset) begin refresh_counter <= refresh_counter + 1'd1;
powerup_coutner <= 14'd0;
powerup_done <= 1'b0; if (refresh_counter == C_INIT) begin
end else if (powerup_coutner < C_INIT) begin refresh_counter <= 14'd0;
powerup_coutner <= powerup_coutner + 1'd1;
end else begin
powerup_done <= 1'b1; powerup_done <= 1'b1;
end end
if (reset || state != next_state) begin if (powerup_done && refresh_counter == C_REF - 14'd1) begin
wait_counter <= 5'd0; refresh_counter <= 14'd0;
end else begin pending_refresh <= 1'b1;
wait_counter <= wait_counter + 1'd1;
end end
if (sdram_next_cmd == CMD_REF) begin if (sdram_next_cmd == CMD_REF) begin
refresh_counter <= 10'd0;
pending_refresh <= 1'b0; pending_refresh <= 1'b0;
end else if (refresh_counter < C_REF) begin end
refresh_counter <= refresh_counter + 1'd1;
end else begin if (reset) begin
pending_refresh <= 1'b1; refresh_counter <= 14'd0;
powerup_done <= 1'b0;
pending_refresh <= 1'b0;
end
wait_counter <= wait_counter + 1'd1;
if (state != next_state) begin
wait_counter <= 5'd0;
end
precharge_counter <= precharge_counter + 1'd1;
if (precharge_counter >= C_RAS - 2'd2) begin
precharge_valid <= 1'b1;
end
if (sdram_next_cmd == CMD_ACT) begin
precharge_counter <= 3'd0;
precharge_valid <= 1'b0;
end end
end end
@ -157,6 +191,7 @@ module memory_sdram (
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
mem_bus.ack <= 1'b0; mem_bus.ack <= 1'b0;
read_cmd_ack_delay <= {sdram_next_cmd == CMD_READ, read_cmd_ack_delay[(CAS_LATENCY):1]}; read_cmd_ack_delay <= {sdram_next_cmd == CMD_READ, read_cmd_ack_delay[(CAS_LATENCY):1]};
if (sdram_next_cmd == CMD_WRITE || read_cmd_ack_delay[0]) begin if (sdram_next_cmd == CMD_WRITE || read_cmd_ack_delay[0]) begin
@ -202,20 +237,20 @@ module memory_sdram (
end end
S_ACTIVATING: begin S_ACTIVATING: begin
if (wait_counter == C_RCD) begin if (wait_counter == C_RCD - 5'd2) begin
next_state = S_ACTIVE; next_state = S_ACTIVE;
end end
end end
S_ACTIVE: begin S_ACTIVE: begin
if (pending_refresh) begin if (pending_refresh && precharge_valid) begin
next_state = S_PRECHARGE; next_state = S_PRECHARGE;
sdram_next_cmd = CMD_PRE; sdram_next_cmd = CMD_PRE;
end else if (mem_bus.request) begin end else if (mem_bus.request) begin
if (request_in_current_active_bank_row) begin if (request_in_current_active_bank_row) begin
next_state = S_BUSY; next_state = S_BUSY;
sdram_next_cmd = mem_bus.write ? CMD_WRITE : CMD_READ; sdram_next_cmd = mem_bus.write ? CMD_WRITE : CMD_READ;
end else begin end else if (precharge_valid) begin
next_state = S_PRECHARGE; next_state = S_PRECHARGE;
sdram_next_cmd = CMD_PRE; sdram_next_cmd = CMD_PRE;
end end
@ -229,18 +264,13 @@ module memory_sdram (
end end
S_PRECHARGE: begin S_PRECHARGE: begin
if (wait_counter == C_RP) begin if (wait_counter == C_RP - 5'd2) begin
if (pending_refresh) begin next_state = S_IDLE;
next_state = S_REFRESH;
sdram_next_cmd = CMD_REF;
end else begin
next_state = S_IDLE;
end
end end
end end
S_REFRESH: begin S_REFRESH: begin
if (wait_counter == C_RC) begin if (wait_counter == C_RC - 5'd2) begin
next_state = S_IDLE; next_state = S_IDLE;
end end
end end

View File

@ -9,7 +9,7 @@ module n64_cfg (
output logic irq output logic irq
); );
typedef enum bit [3:0] { typedef enum bit [3:0] {
REG_STATUS, REG_STATUS,
REG_COMMAND, REG_COMMAND,
REG_DATA_0_H, REG_DATA_0_H,
@ -19,10 +19,39 @@ module n64_cfg (
REG_IDENTIFIER_H, REG_IDENTIFIER_H,
REG_IDENTIFIER_L, REG_IDENTIFIER_L,
REG_KEY_H, REG_KEY_H,
REG_KEY_L REG_KEY_L,
REG_IRQ_H,
REG_IRQ_L,
REG_AUX_H,
REG_AUX_L
} e_reg; } e_reg;
logic cfg_error; logic cmd_error;
logic cmd_irq_request;
logic cmd_irq;
logic btn_irq;
logic usb_irq;
logic aux_irq;
logic btn_irq_mask;
logic cmd_irq_mask;
logic usb_irq_mask;
logic aux_irq_mask;
always_ff @(posedge clk) begin
irq <= (
(btn_irq && btn_irq_mask) ||
(cmd_irq && cmd_irq_mask) ||
(usb_irq && usb_irq_mask) ||
(aux_irq && aux_irq_mask)
);
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;
@ -30,11 +59,18 @@ module n64_cfg (
case (reg_bus.address[4:1]) case (reg_bus.address[4:1])
REG_STATUS: reg_bus.rdata = { REG_STATUS: reg_bus.rdata = {
n64_scb.cfg_pending, n64_scb.cfg_pending,
cfg_error, cmd_error,
irq, btn_irq,
13'd0 btn_irq_mask,
cmd_irq,
cmd_irq_mask,
usb_irq,
usb_irq_mask,
aux_irq,
aux_irq_mask,
6'd0
}; };
REG_COMMAND: reg_bus.rdata = {8'd0, 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];
REG_DATA_0_L: reg_bus.rdata = n64_scb.cfg_wdata[0][15:0]; REG_DATA_0_L: reg_bus.rdata = n64_scb.cfg_wdata[0][15:0];
REG_DATA_1_H: reg_bus.rdata = n64_scb.cfg_wdata[1][31:16]; REG_DATA_1_H: reg_bus.rdata = n64_scb.cfg_wdata[1][31:16];
@ -43,6 +79,10 @@ 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 = 16'd0;
REG_IRQ_L: reg_bus.rdata = 16'd0;
REG_AUX_H: reg_bus.rdata = n64_scb.aux_wdata[31:16];
REG_AUX_L: reg_bus.rdata = n64_scb.aux_wdata[15:0];
endcase endcase
end end
end end
@ -51,48 +91,145 @@ module n64_cfg (
logic lock_sequence_counter; logic lock_sequence_counter;
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
if (n64_scb.cfg_done) begin n64_scb.aux_pending <= 1'b0;
if (n64_scb.cfg_pending && n64_scb.cfg_done) begin
n64_scb.cfg_pending <= 1'b0; n64_scb.cfg_pending <= 1'b0;
cfg_error <= n64_scb.cfg_error; cmd_irq <= cmd_irq_request;
cmd_error <= n64_scb.cfg_error;
end end
if (n64_scb.cfg_irq) begin if (n64_scb.cfg_unlock) begin
irq <= 1'b1; if (n64_scb.btn_irq) begin
btn_irq <= 1'b1;
end
if (n64_scb.usb_irq) begin
usb_irq <= 1'b1;
end
if (n64_scb.aux_irq) begin
aux_irq <= 1'b1;
end
end end
if (unlock_flag) begin if (unlock_flag) begin
n64_scb.cfg_unlock <= 1'b1; n64_scb.cfg_unlock <= 1'b1;
end end
if (reset || n64_scb.n64_reset || n64_scb.n64_nmi) begin if (reset) begin
n64_scb.cfg_unlock <= 1'b0; n64_scb.cfg_unlock <= 1'b0;
n64_scb.cfg_pending <= 1'b0; n64_scb.cfg_pending <= 1'b0;
n64_scb.cfg_cmd <= 8'h00; n64_scb.cfg_cmd <= 8'h00;
irq <= 1'b0; cmd_error <= 1'b0;
cfg_error <= 1'b0; cmd_irq_request <= 1'b0;
btn_irq <= 1'b0;
cmd_irq <= 1'b0;
usb_irq <= 1'b0;
aux_irq <= 1'b0;
usb_irq_mask <= 1'b0;
aux_irq_mask <= 1'b0;
lock_sequence_counter <= 1'd0;
end else if (n64_scb.n64_reset || n64_scb.n64_nmi) begin
n64_scb.cfg_unlock <= 1'b0;
cmd_irq_request <= 1'b0;
btn_irq <= 1'b0;
cmd_irq <= 1'b0;
usb_irq <= 1'b0;
aux_irq <= 1'b0;
usb_irq_mask <= 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
case (reg_bus.address[4:1]) case (reg_bus.address[4:1])
REG_COMMAND: begin REG_COMMAND: begin
n64_scb.cfg_pending <= 1'b1; if (!n64_scb.cfg_pending) begin
n64_scb.cfg_cmd <= reg_bus.wdata[7:0]; n64_scb.cfg_pending <= 1'b1;
cfg_error <= 1'b0; cmd_error <= 1'b0;
cmd_irq_request <= reg_bus.wdata[8];
n64_scb.cfg_cmd <= reg_bus.wdata[7:0];
end
end end
REG_DATA_0_H: n64_scb.cfg_rdata[0][31:16] <= reg_bus.wdata;
REG_DATA_0_L: n64_scb.cfg_rdata[0][15:0] <= reg_bus.wdata; REG_DATA_0_H: begin
REG_DATA_1_H: n64_scb.cfg_rdata[1][31:16] <= reg_bus.wdata; if (!n64_scb.cfg_pending) begin
REG_DATA_1_L: n64_scb.cfg_rdata[1][15:0] <= reg_bus.wdata; n64_scb.cfg_rdata[0][31:16] <= reg_bus.wdata;
REG_IDENTIFIER_H: irq <= 1'b0; end
end
REG_DATA_0_L: begin
if (!n64_scb.cfg_pending) begin
n64_scb.cfg_rdata[0][15:0] <= reg_bus.wdata;
end
end
REG_DATA_1_H: begin
if (!n64_scb.cfg_pending) begin
n64_scb.cfg_rdata[1][31:16] <= reg_bus.wdata;
end
end
REG_DATA_1_L: begin
if (!n64_scb.cfg_pending) begin
n64_scb.cfg_rdata[1][15:0] <= reg_bus.wdata;
end
end
REG_IDENTIFIER_H: begin
btn_irq <= 1'b0;
end
REG_KEY_H, REG_KEY_L: begin REG_KEY_H, REG_KEY_L: begin
lock_sequence_counter <= lock_sequence_counter + 1'd1; lock_sequence_counter <= lock_sequence_counter + 1'd1;
if (reg_bus.wdata != 16'hFFFF) begin if (reg_bus.wdata != 16'hFFFF) begin
lock_sequence_counter <= 1'd0; lock_sequence_counter <= 1'd0;
end end
if (lock_sequence_counter == 1'd1) begin if (lock_sequence_counter == 1'd1) begin
n64_scb.cfg_unlock <= (reg_bus.wdata != 16'hFFFF); if (reg_bus.wdata == 16'hFFFF) begin
n64_scb.cfg_unlock <= 1'b0;
cmd_irq_request <= 1'b0;
btn_irq <= 1'b0;
cmd_irq <= 1'b0;
usb_irq <= 1'b0;
aux_irq <= 1'b0;
usb_irq_mask <= 1'b0;
aux_irq_mask <= 1'b0;
end
end end
end end
REG_IRQ_H: begin
btn_irq <= (reg_bus.wdata[15] ? 1'b0 : btn_irq);
cmd_irq <= (reg_bus.wdata[14] ? 1'b0 : cmd_irq);
usb_irq <= (reg_bus.wdata[13] ? 1'b0 : usb_irq);
aux_irq <= (reg_bus.wdata[12] ? 1'b0 : aux_irq);
end
REG_IRQ_L: begin
if (reg_bus.wdata[10]) begin
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
if (reg_bus.wdata[9]) begin
aux_irq_mask <= 1'b0;
end
end
REG_AUX_H: begin
n64_scb.aux_rdata[31:16] <= reg_bus.wdata;
end
REG_AUX_L: begin
n64_scb.aux_pending <= 1'b1;
n64_scb.aux_rdata[15:0] <= reg_bus.wdata;
end
endcase endcase
end end
end end

View File

@ -6,7 +6,8 @@ module n64_cic (
input n64_reset, input n64_reset,
input n64_cic_clk, input n64_cic_clk,
inout n64_cic_dq inout n64_cic_dq,
input n64_si_clk
); );
// Input/output synchronization // Input/output synchronization
@ -14,26 +15,83 @@ module n64_cic (
logic [1:0] n64_reset_ff; logic [1:0] n64_reset_ff;
logic [1:0] n64_cic_clk_ff; logic [1:0] n64_cic_clk_ff;
logic [1:0] n64_cic_dq_ff; logic [1:0] n64_cic_dq_ff;
logic [1:0] n64_si_clk_ff;
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
n64_reset_ff <= {n64_reset_ff[0], n64_reset}; n64_reset_ff <= {n64_reset_ff[0], n64_reset};
n64_cic_clk_ff <= {n64_cic_clk_ff[0], n64_cic_clk}; n64_cic_clk_ff <= {n64_cic_clk_ff[0], n64_cic_clk};
n64_cic_dq_ff <= {n64_cic_dq_ff[0], n64_cic_dq}; n64_cic_dq_ff <= {n64_cic_dq_ff[0], n64_cic_dq};
n64_si_clk_ff <= {n64_si_clk_ff[0], n64_si_clk};
end end
logic cic_reset; logic cic_reset;
logic cic_clk; logic cic_clk;
logic cic_dq; logic cic_dq;
logic si_clk;
always_comb begin always_comb begin
cic_reset = n64_reset_ff[1]; cic_reset = n64_reset_ff[1];
cic_clk = n64_cic_clk_ff[1]; cic_clk = n64_cic_clk_ff[1];
cic_dq = n64_cic_dq_ff[1]; cic_dq = n64_cic_dq_ff[1];
si_clk = n64_si_clk_ff[1];
end end
logic cic_dq_out; logic cic_dq_out;
logic cic_dq_oe;
assign n64_cic_dq = cic_dq_out ? 1'bZ : 1'b0; always_ff @(posedge clk) begin
cic_dq_oe <= ~cic_dq_out;
end
assign n64_cic_dq = cic_dq_oe ? 1'b0 : 1'bZ;
// Timer (divider and counter)
logic last_si_clk;
always_ff @(posedge clk) begin
last_si_clk <= si_clk;
end
logic si_clk_rising_edge;
always_comb begin
si_clk_rising_edge = cic_reset && !last_si_clk && si_clk;
end
logic [7:0] timer_divider;
logic [11:0] timer_counter;
logic timer_start;
logic timer_clear;
logic timer_elapsed;
const bit [11:0] TIMEOUT_500MS = 12'd3815; // (62_500_000 / 32 / 256 / 2) = ~500 ms
always_ff @(posedge clk) begin
if (si_clk_rising_edge) begin
timer_divider <= timer_divider + 1'd1;
end
if (si_clk_rising_edge && (&timer_divider) && (timer_counter > 12'd0)) begin
timer_counter <= timer_counter - 1'd1;
if (timer_counter == 12'd1) begin
timer_elapsed <= 1'b1;
end
end
if (timer_start) begin
timer_divider <= 8'd0;
timer_counter <= TIMEOUT_500MS;
timer_elapsed <= 1'b0;
end
if (timer_clear) begin
timer_divider <= 8'd0;
timer_counter <= 12'd0;
timer_elapsed <= 1'b0;
end
end
// SERV RISC-V CPU // SERV RISC-V CPU
@ -98,7 +156,6 @@ module n64_cic (
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
ram_output <= ram[ram_addr]; ram_output <= ram[ram_addr];
ibus_ack <= ibus_cycle && !ibus_ack; ibus_ack <= ibus_cycle && !ibus_ack;
end end
@ -110,6 +167,8 @@ module n64_cic (
// Bus controller // Bus controller
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
timer_start <= 1'b0;
timer_clear <= 1'b0;
n64_scb.cic_invalid_region <= 1'b0; n64_scb.cic_invalid_region <= 1'b0;
dbus_ack <= dbus_cycle && !dbus_ack; dbus_ack <= dbus_cycle && !dbus_ack;
@ -126,14 +185,24 @@ module n64_cic (
2'b11: begin 2'b11: begin
case (dbus_addr[3:2]) case (dbus_addr[3:2])
2'b10: begin 2'b10: begin
n64_scb.cic_invalid_region <= dbus_wdata[3]; n64_scb.cic_invalid_region <= dbus_wdata[6];
timer_clear <= dbus_wdata[5];
timer_start <= dbus_wdata[4];
cic_dq_out <= dbus_wdata[0]; cic_dq_out <= dbus_wdata[0];
end end
2'b11: begin
n64_scb.cic_debug_step <= dbus_wdata[3:0];
end
endcase endcase
end end
endcase endcase
end end
if (reset) begin
n64_scb.cic_debug_step <= 3'd0;
end
if (reset || !cic_reset) begin if (reset || !cic_reset) begin
cic_dq_out <= 1'b1; cic_dq_out <= 1'b1;
end end
@ -159,7 +228,15 @@ module n64_cic (
2'b01: dbus_rdata = n64_scb.cic_checksum[31:0]; 2'b01: dbus_rdata = n64_scb.cic_checksum[31:0];
2'b10: dbus_rdata = {29'd0, cic_reset, cic_clk, cic_dq}; 2'b10: dbus_rdata = {
28'd0,
timer_elapsed,
cic_reset,
cic_clk,
cic_dq
};
2'b11: dbus_rdata = {28'd0, n64_scb.cic_debug_step};
endcase endcase
end end
endcase endcase

View File

@ -8,7 +8,7 @@ module n64_flashram (
); );
localparam [31:0] FLASH_TYPE_ID = 32'h1111_8001; localparam [31:0] FLASH_TYPE_ID = 32'h1111_8001;
localparam [31:0] FLASH_MODEL_ID = 32'h00C2_001D; localparam [31:0] FLASH_MODEL_ID = 32'h0032_00F1;
typedef enum bit [7:0] { typedef enum bit [7:0] {
CMD_STATUS_MODE = 8'hD2, CMD_STATUS_MODE = 8'hD2,
@ -97,14 +97,14 @@ module n64_flashram (
CMD_ERASE_SECTOR: begin CMD_ERASE_SECTOR: begin
state <= STATE_STATUS; state <= STATE_STATUS;
erase_enabled <= 1'b1; erase_enabled <= 1'b1;
n64_scb.flashram_sector <= reg_bus.wdata[9:0]; n64_scb.flashram_page <= reg_bus.wdata[9:0];
n64_scb.flashram_sector_or_all <= 1'b0; n64_scb.flashram_sector_or_all <= 1'b0;
end end
CMD_ERASE_CHIP: begin CMD_ERASE_CHIP: begin
state <= STATE_STATUS; state <= STATE_STATUS;
erase_enabled <= 1'b1; erase_enabled <= 1'b1;
n64_scb.flashram_sector <= 10'd0; n64_scb.flashram_page <= 10'd0;
n64_scb.flashram_sector_or_all <= 1'b1; n64_scb.flashram_sector_or_all <= 1'b1;
end end
@ -126,7 +126,7 @@ module n64_flashram (
state <= STATE_STATUS; state <= STATE_STATUS;
status[WRITE_BUSY] <= 1'b1; status[WRITE_BUSY] <= 1'b1;
status[WRITE_DONE] <= 1'b0; status[WRITE_DONE] <= 1'b0;
n64_scb.flashram_sector <= reg_bus.wdata[9:0]; n64_scb.flashram_page <= reg_bus.wdata[9:0];
n64_scb.flashram_pending <= 1'b1; n64_scb.flashram_pending <= 1'b1;
n64_scb.flashram_write_or_erase <= 1'b0; n64_scb.flashram_write_or_erase <= 1'b0;
n64_scb.flashram_sector_or_all <= 1'b0; n64_scb.flashram_sector_or_all <= 1'b0;
@ -134,9 +134,9 @@ module n64_flashram (
endcase endcase
end end
end else begin end else begin
if (reg_bus.address[1] && state != STATE_BUFFER) begin if (reg_bus.address[1] && state == STATE_STATUS) begin
status[ERASE_BUSY] <= reg_bus.wdata[ERASE_BUSY]; status[ERASE_DONE] <= 1'b0;
status[WRITE_BUSY] <= reg_bus.wdata[WRITE_BUSY]; status[WRITE_DONE] <= 1'b0;
end end
end end
end end

View File

@ -127,12 +127,21 @@ module n64_pi (
// Debug: last accessed PI address // Debug: last accessed PI address
logic [15:0] pi_debug_address_buffer;
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
if (aleh_op) begin if (aleh_op) begin
n64_scb.pi_debug[31:16] <= n64_pi_dq_in; pi_debug_address_buffer <= n64_pi_dq_in;
end end
if (alel_op) begin if (alel_op) begin
n64_scb.pi_debug[15:0] <= n64_pi_dq_in; n64_scb.pi_debug_address <= {pi_debug_address_buffer, n64_pi_dq_in};
n64_scb.pi_debug_rw_count <= 17'd0;
end
if (pi_reset && (pi_mode == PI_MODE_VALID)) begin
if ((last_read && !pi_read) || (last_write && !pi_write)) begin
n64_scb.pi_debug_rw_count <= n64_scb.pi_debug_rw_count + 1'd1;
n64_scb.pi_debug_direction <= !pi_write;
end
end end
end end
@ -296,11 +305,11 @@ module n64_pi (
.reset(reset), .reset(reset),
.flush(reset || !pi_reset || alel_op), .flush(reset || !pi_reset || alel_op),
.full(read_fifo_full), .full(read_fifo_full),
.write(read_fifo_write), .write(read_fifo_write),
.wdata(read_fifo_wdata), .wdata(read_fifo_wdata),
.empty(read_fifo_empty), .empty(read_fifo_empty),
.read(read_fifo_read), .read(read_fifo_read),
.rdata(read_fifo_rdata) .rdata(read_fifo_rdata)
@ -310,7 +319,7 @@ module n64_pi (
read_fifo_read <= 1'b0; read_fifo_read <= 1'b0;
if (!pi_reset) begin if (!pi_reset) begin
n64_scb.pi_debug[33:32] <= 2'b00; n64_scb.pi_debug_fifo_flags[1:0] <= 2'b00;
end end
if (reset || !pi_reset || alel_op) begin if (reset || !pi_reset || alel_op) begin
@ -321,9 +330,9 @@ module n64_pi (
if (read_op) begin if (read_op) begin
if (read_fifo_empty) begin if (read_fifo_empty) begin
read_fifo_wait <= 1'b1; read_fifo_wait <= 1'b1;
n64_scb.pi_debug[32] <= 1'b1; n64_scb.pi_debug_fifo_flags[0] <= 1'b1;
if (read_fifo_wait) begin if (read_fifo_wait) begin
n64_scb.pi_debug[33] <= 1'b1; n64_scb.pi_debug_fifo_flags[1] <= 1'b1;
end end
end else begin end else begin
read_fifo_read <= 1'b1; read_fifo_read <= 1'b1;
@ -377,7 +386,7 @@ module n64_pi (
write_fifo_write <= 1'b0; write_fifo_write <= 1'b0;
if (!pi_reset) begin if (!pi_reset) begin
n64_scb.pi_debug[35:34] <= 2'b00; n64_scb.pi_debug_fifo_flags[3:2] <= 2'b00;
end end
if (reset) begin if (reset) begin
@ -388,9 +397,9 @@ module n64_pi (
if (write_op) begin if (write_op) begin
if (write_fifo_full) begin if (write_fifo_full) begin
write_fifo_wait <= 1'b1; write_fifo_wait <= 1'b1;
n64_scb.pi_debug[34] <= 1'b1; n64_scb.pi_debug_fifo_flags[2] <= 1'b1;
if (write_fifo_wait) begin if (write_fifo_wait) begin
n64_scb.pi_debug[35] <= 1'b1; n64_scb.pi_debug_fifo_flags[3] <= 1'b1;
end end
end else begin end else begin
write_fifo_write <= 1'b1; write_fifo_write <= 1'b1;
@ -476,7 +485,11 @@ module n64_pi (
// Reg bus controller // Reg bus controller
logic reg_bus_address_increment;
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
reg_bus_address_increment <= read_op || write_op;
if (aleh_op) begin if (aleh_op) begin
reg_bus.address[16] <= n64_pi_dq_in[0]; reg_bus.address[16] <= n64_pi_dq_in[0];
end end
@ -485,15 +498,13 @@ module n64_pi (
reg_bus.address[15:0] <= n64_pi_dq_in; reg_bus.address[15:0] <= n64_pi_dq_in;
end end
if (read_op || write_op) begin if (reg_bus_address_increment) begin
reg_bus.address <= reg_bus.address + 2'd2; reg_bus.address <= reg_bus.address + 2'd2;
end end
end
always_comb begin reg_bus.read <= read_op && (read_port == PORT_REG);
reg_bus.read = read_op && (read_port == PORT_REG); reg_bus.write <= write_op && (write_port == PORT_REG);
reg_bus.write = write_op && (write_port == PORT_REG); reg_bus.wdata <= n64_pi_dq_in;
reg_bus.wdata = n64_pi_dq_in;
end end
endmodule endmodule

View File

@ -22,7 +22,7 @@ interface n64_scb ();
logic flashram_pending; logic flashram_pending;
logic flashram_done; logic flashram_done;
logic [9:0] flashram_sector; logic [9:0] flashram_page;
logic flashram_sector_or_all; logic flashram_sector_or_all;
logic flashram_write_or_erase; logic flashram_write_or_erase;
logic flashram_read_mode; logic flashram_read_mode;
@ -40,19 +40,26 @@ interface n64_scb ();
logic rtc_pending; logic rtc_pending;
logic rtc_done; logic rtc_done;
logic rtc_wdata_valid; logic rtc_wdata_valid;
logic [41:0] rtc_rdata; logic [42:0] rtc_rdata;
logic [41:0] rtc_wdata; logic [42:0] rtc_wdata;
logic cfg_unlock; logic cfg_unlock;
logic cfg_pending; logic cfg_pending;
logic cfg_done; logic cfg_done;
logic cfg_error; logic cfg_error;
logic cfg_irq;
logic [7:0] cfg_cmd; logic [7:0] cfg_cmd;
logic [31:0] cfg_rdata [0:1]; logic [31:0] cfg_rdata [0:1];
logic [31:0] cfg_wdata [0:1]; logic [31:0] cfg_wdata [0:1];
logic [31:0] cfg_identifier; logic [31:0] cfg_identifier;
logic btn_irq;
logic usb_irq;
logic aux_irq;
logic aux_pending;
logic [31:0] aux_rdata;
logic [31:0] aux_wdata;
logic [15:0] save_count; logic [15:0] save_count;
logic cic_invalid_region; logic cic_invalid_region;
@ -61,10 +68,14 @@ interface n64_scb ();
logic cic_region; logic cic_region;
logic [7:0] cic_seed; logic [7:0] cic_seed;
logic [47:0] cic_checksum; logic [47:0] cic_checksum;
logic [3:0] cic_debug_step;
logic pi_sdram_active; logic pi_sdram_active;
logic pi_flash_active; logic pi_flash_active;
logic [35:0] pi_debug; logic [31:0] pi_debug_address;
logic [16:0] pi_debug_rw_count;
logic pi_debug_direction;
logic [3:0] pi_debug_fifo_flags;
modport controller ( modport controller (
input n64_reset, input n64_reset,
@ -84,7 +95,7 @@ interface n64_scb ();
input flashram_pending, input flashram_pending,
output flashram_done, output flashram_done,
input flashram_sector, input flashram_page,
input flashram_sector_or_all, input flashram_sector_or_all,
input flashram_write_or_erase, input flashram_write_or_erase,
@ -97,12 +108,19 @@ interface n64_scb ();
input cfg_pending, input cfg_pending,
output cfg_done, output cfg_done,
output cfg_error, output cfg_error,
output cfg_irq,
input cfg_cmd, input cfg_cmd,
input cfg_rdata, input cfg_rdata,
output cfg_wdata, output cfg_wdata,
output cfg_identifier, output cfg_identifier,
output btn_irq,
output usb_irq,
output aux_irq,
input aux_pending,
input aux_rdata,
output aux_wdata,
input save_count, input save_count,
input cic_invalid_region, input cic_invalid_region,
@ -111,8 +129,12 @@ interface n64_scb ();
output cic_region, output cic_region,
output cic_seed, output cic_seed,
output cic_checksum, output cic_checksum,
input cic_debug_step,
input pi_debug input pi_debug_address,
input pi_debug_rw_count,
input pi_debug_direction,
input pi_debug_fifo_flags
); );
modport pi ( modport pi (
@ -137,13 +159,17 @@ interface n64_scb ();
output pi_sdram_active, output pi_sdram_active,
output pi_flash_active, output pi_flash_active,
output pi_debug
output pi_debug_address,
output pi_debug_rw_count,
output pi_debug_direction,
output pi_debug_fifo_flags
); );
modport flashram ( modport flashram (
output flashram_pending, output flashram_pending,
input flashram_done, input flashram_done,
output flashram_sector, output flashram_page,
output flashram_sector_or_all, output flashram_sector_or_all,
output flashram_write_or_erase, output flashram_write_or_erase,
@ -204,11 +230,18 @@ interface n64_scb ();
output cfg_pending, output cfg_pending,
input cfg_done, input cfg_done,
input cfg_error, input cfg_error,
input cfg_irq,
output cfg_cmd, output cfg_cmd,
output cfg_rdata, output cfg_rdata,
input cfg_wdata, input cfg_wdata,
input cfg_identifier input cfg_identifier,
input btn_irq,
input usb_irq,
input aux_irq,
output aux_pending,
output aux_rdata,
input aux_wdata
); );
modport save_counter ( modport save_counter (
@ -225,7 +258,8 @@ interface n64_scb ();
input cic_64dd_mode, input cic_64dd_mode,
input cic_region, input cic_region,
input cic_seed, input cic_seed,
input cic_checksum input cic_checksum,
output cic_debug_step
); );
modport arbiter ( modport arbiter (

View File

@ -59,6 +59,7 @@ module n64_si (
// Data falling/rising event generator // Data falling/rising event generator
logic last_si_dq_in; logic last_si_dq_in;
logic si_dq_in_inhibit;
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
if (si_clk_rising_edge) begin if (si_clk_rising_edge) begin
@ -70,14 +71,14 @@ module n64_si (
logic si_dq_rising_edge; logic si_dq_rising_edge;
always_comb begin always_comb begin
si_dq_falling_edge = si_clk_rising_edge && last_si_dq_in && !si_dq_in; si_dq_falling_edge = si_clk_rising_edge && last_si_dq_in && !si_dq_in && !si_dq_in_inhibit;
si_dq_rising_edge = si_clk_rising_edge && !last_si_dq_in && si_dq_in; si_dq_rising_edge = si_clk_rising_edge && !last_si_dq_in && si_dq_in && !si_dq_in_inhibit;
end end
// RX bit generator // RX bit generator
logic [3:0] rx_sub_bit_counter; logic [4:0] rx_sub_bit_counter;
logic rx_timeout; logic rx_timeout;
logic rx_bit_valid; logic rx_bit_valid;
logic rx_bit_data; logic rx_bit_data;
@ -94,7 +95,7 @@ module n64_si (
always_comb begin always_comb begin
rx_timeout = si_clk_rising_edge && si_dq_in && (&rx_sub_bit_counter); rx_timeout = si_clk_rising_edge && si_dq_in && (&rx_sub_bit_counter);
rx_bit_valid = si_dq_rising_edge; rx_bit_valid = si_dq_rising_edge;
rx_bit_data = (rx_sub_bit_counter >= 4'd3) ? 1'b0 : 1'b1; rx_bit_data = (rx_sub_bit_counter >= 5'd4) ? 1'b0 : 1'b1;
end end
@ -124,7 +125,7 @@ module n64_si (
logic rx_stop; logic rx_stop;
always_comb begin always_comb begin
rx_stop = si_clk_rising_edge && si_dq_in && (rx_sub_bit_counter == 4'd7) && (rx_bit_counter == 3'd1); rx_stop = si_clk_rising_edge && si_dq_in && (rx_sub_bit_counter == 5'd15) && (rx_bit_counter == 3'd1);
end end
@ -260,7 +261,8 @@ module n64_si (
typedef enum bit [1:0] { typedef enum bit [1:0] {
TX_STATE_IDLE, TX_STATE_IDLE,
TX_STATE_DATA, TX_STATE_DATA,
TX_STATE_STOP TX_STATE_STOP,
TX_STATE_STOP_WAIT
} e_tx_state; } e_tx_state;
e_tx_state tx_state; e_tx_state tx_state;
@ -278,12 +280,14 @@ module n64_si (
if (reset) begin if (reset) begin
tx_state <= TX_STATE_IDLE; tx_state <= TX_STATE_IDLE;
si_dq_in_inhibit <= 1'b0;
end else begin end else begin
case (tx_state) case (tx_state)
TX_STATE_IDLE: begin TX_STATE_IDLE: begin
if (tx_start) begin if (tx_start) begin
tx_byte_counter <= 4'd0; tx_byte_counter <= 4'd0;
tx_state <= TX_STATE_DATA; tx_state <= TX_STATE_DATA;
si_dq_in_inhibit <= 1'b1;
end end
end end
@ -299,7 +303,14 @@ module n64_si (
TX_STATE_STOP: begin TX_STATE_STOP: begin
tx_stop <= 1'b1; tx_stop <= 1'b1;
if (!tx_busy && tx_stop) begin if (!tx_busy && tx_stop) begin
tx_state <= TX_STATE_STOP_WAIT;
end
end
TX_STATE_STOP_WAIT: begin
if (!tx_busy) begin
tx_state <= TX_STATE_IDLE; tx_state <= TX_STATE_IDLE;
si_dq_in_inhibit <= 1'b0;
end end
end end
endcase endcase
@ -351,6 +362,7 @@ module n64_si (
logic [2:0] rtc_time_weekday; logic [2:0] rtc_time_weekday;
logic [4:0] rtc_time_month; logic [4:0] rtc_time_month;
logic [7:0] rtc_time_year; logic [7:0] rtc_time_year;
logic rtc_time_century;
always_ff @(posedge clk) begin always_ff @(posedge clk) begin
if (reset) begin if (reset) begin
@ -366,6 +378,7 @@ module n64_si (
if (!(|rtc_stopped) && !n64_scb.rtc_pending && n64_scb.rtc_wdata_valid && (tx_state != TX_STATE_DATA)) begin if (!(|rtc_stopped) && !n64_scb.rtc_pending && n64_scb.rtc_wdata_valid && (tx_state != TX_STATE_DATA)) begin
{ {
rtc_time_century,
rtc_time_year, rtc_time_year,
rtc_time_month, rtc_time_month,
rtc_time_weekday, rtc_time_weekday,
@ -382,7 +395,7 @@ module n64_si (
4'd1: {rtc_time_wp, rtc_backup_wp} <= rx_byte_data[1:0]; 4'd1: {rtc_time_wp, rtc_backup_wp} <= rx_byte_data[1:0];
4'd2: begin 4'd2: begin
rtc_stopped <= rx_byte_data[2:1]; rtc_stopped <= rx_byte_data[2:1];
if (rx_byte_data[2:1] == 2'b00) begin if ((|rtc_stopped) && (rx_byte_data[2:1] == 2'b00)) begin
n64_scb.rtc_pending <= 1'b1; n64_scb.rtc_pending <= 1'b1;
end end
end end
@ -397,6 +410,7 @@ module n64_si (
4'd5: rtc_time_weekday <= rx_byte_data[2:0]; 4'd5: rtc_time_weekday <= rx_byte_data[2:0];
4'd6: rtc_time_month <= rx_byte_data[4:0]; 4'd6: rtc_time_month <= rx_byte_data[4:0];
4'd7: rtc_time_year <= rx_byte_data; 4'd7: rtc_time_year <= rx_byte_data;
4'd8: rtc_time_century <= rx_byte_data[0];
endcase endcase
end end
end end
@ -404,6 +418,7 @@ module n64_si (
always_comb begin always_comb begin
n64_scb.rtc_rdata = { n64_scb.rtc_rdata = {
rtc_time_century,
rtc_time_year, rtc_time_year,
rtc_time_month, rtc_time_month,
rtc_time_weekday, rtc_time_weekday,
@ -458,7 +473,7 @@ module n64_si (
4'd4: tx_byte_data = {5'd0, rtc_time_weekday}; 4'd4: tx_byte_data = {5'd0, rtc_time_weekday};
4'd5: tx_byte_data = {3'd0, rtc_time_month}; 4'd5: tx_byte_data = {3'd0, rtc_time_month};
4'd6: tx_byte_data = rtc_time_year; 4'd6: tx_byte_data = rtc_time_year;
4'd7: tx_byte_data = 8'h01; 4'd7: tx_byte_data = {7'd0, rtc_time_century};
4'd8: tx_byte_data = {(|rtc_stopped), 7'd0}; 4'd8: tx_byte_data = {(|rtc_stopped), 7'd0};
endcase endcase
end end

View File

@ -26,13 +26,19 @@ module n64_top (
logic n64_dd_irq; logic n64_dd_irq;
logic n64_cfg_irq; logic n64_cfg_irq;
logic n64_irq_oe;
logic irq_data;
logic irq_dq;
logic [1:0] irq_oe;
assign irq_data = (n64_dd_irq || n64_cfg_irq);
always @(posedge clk) begin always @(posedge clk) begin
n64_irq_oe <= (n64_dd_irq || n64_cfg_irq); irq_dq <= (~irq_data);
irq_oe <= {irq_oe[0], irq_data};
end end
assign n64_irq = n64_irq_oe ? 1'b0 : 1'bZ; assign n64_irq = irq_oe[1] ? irq_dq : 1'bZ;
n64_reg_bus reg_bus (); n64_reg_bus reg_bus ();
@ -112,7 +118,8 @@ module n64_top (
.n64_reset(n64_reset), .n64_reset(n64_reset),
.n64_cic_clk(n64_cic_clk), .n64_cic_clk(n64_cic_clk),
.n64_cic_dq(n64_cic_dq) .n64_cic_dq(n64_cic_dq),
.n64_si_clk(n64_si_clk)
); );
endmodule endmodule

View File

@ -36,12 +36,10 @@ module sd_dat (
// FIFO // FIFO
logic rx_full; logic rx_full;
logic rx_almost_full;
logic rx_write; logic rx_write;
logic [7:0] rx_wdata; logic [7:0] rx_wdata;
logic tx_empty; logic tx_empty;
logic tx_almost_empty;
logic tx_read; logic tx_read;
logic [7:0] tx_rdata; logic [7:0] tx_rdata;
@ -50,12 +48,10 @@ module sd_dat (
.reset(reset || sd_scb.dat_fifo_flush), .reset(reset || sd_scb.dat_fifo_flush),
.empty(fifo_bus.rx_empty), .empty(fifo_bus.rx_empty),
.almost_empty(fifo_bus.rx_almost_empty),
.read(fifo_bus.rx_read), .read(fifo_bus.rx_read),
.rdata(fifo_bus.rx_rdata), .rdata(fifo_bus.rx_rdata),
.full(rx_full), .full(rx_full),
.almost_full(rx_almost_full),
.write(rx_write), .write(rx_write),
.wdata(rx_wdata), .wdata(rx_wdata),
@ -67,12 +63,10 @@ module sd_dat (
.reset(reset || sd_scb.dat_fifo_flush), .reset(reset || sd_scb.dat_fifo_flush),
.empty(tx_empty), .empty(tx_empty),
.almost_empty(tx_almost_empty),
.read(tx_read), .read(tx_read),
.rdata(tx_rdata), .rdata(tx_rdata),
.full(fifo_bus.tx_full), .full(fifo_bus.tx_full),
.almost_full(fifo_bus.tx_almost_full),
.write(fifo_bus.tx_write), .write(fifo_bus.tx_write),
.wdata(fifo_bus.tx_wdata), .wdata(fifo_bus.tx_wdata),

View File

@ -51,7 +51,10 @@ module top (
output mcu_miso, output mcu_miso,
// Unused I/O // Unused I/O
output n64_video_sync
output n64_video_sync,
output [2:0] test_point
); );
logic clk; logic clk;
@ -278,4 +281,6 @@ module top (
assign n64_video_sync = 1'bZ; assign n64_video_sync = 1'bZ;
assign test_point = 3'b000;
endmodule endmodule

View File

@ -1,39 +1,3 @@
interface usb_scb ();
logic fifo_flush;
logic reset_pending;
logic reset_ack;
logic write_buffer_flush;
logic [10:0] rx_count;
logic [10:0] tx_count;
logic pwrsav;
logic reset_state;
modport controller (
output fifo_flush,
input reset_pending,
output reset_ack,
output write_buffer_flush,
input rx_count,
input tx_count,
input pwrsav,
input reset_state
);
modport usb (
input fifo_flush,
output reset_pending,
input reset_ack,
input write_buffer_flush,
output rx_count,
output tx_count,
output pwrsav,
output reset_state
);
endinterface
module usb_ft1248 ( module usb_ft1248 (
input clk, input clk,
input reset, input reset,
@ -50,27 +14,25 @@ module usb_ft1248 (
); );
logic rx_full; logic rx_full;
logic rx_almost_full; logic rx_write_delayed;
logic rx_write;
logic [7:0] rx_wdata; logic [7:0] rx_wdata;
logic tx_empty; logic tx_empty;
logic tx_almost_empty;
logic tx_read; logic tx_read;
logic [7:0] tx_rdata; logic [7:0] tx_rdata;
logic fifo_flush;
fifo_8kb fifo_8kb_rx_inst ( fifo_8kb fifo_8kb_rx_inst (
.clk(clk), .clk(clk),
.reset(reset || usb_scb.fifo_flush), .reset(fifo_flush),
.empty(fifo_bus.rx_empty), .empty(fifo_bus.rx_empty),
.almost_empty(fifo_bus.rx_almost_empty),
.read(fifo_bus.rx_read), .read(fifo_bus.rx_read),
.rdata(fifo_bus.rx_rdata), .rdata(fifo_bus.rx_rdata),
.full(rx_full), .full(rx_full),
.almost_full(rx_almost_full), .write(rx_write_delayed),
.write(rx_write),
.wdata(rx_wdata), .wdata(rx_wdata),
.count(usb_scb.rx_count) .count(usb_scb.rx_count)
@ -78,15 +40,13 @@ module usb_ft1248 (
fifo_8kb fifo_8kb_tx_inst ( fifo_8kb fifo_8kb_tx_inst (
.clk(clk), .clk(clk),
.reset(reset || usb_scb.fifo_flush), .reset(fifo_flush),
.empty(tx_empty), .empty(tx_empty),
.almost_empty(tx_almost_empty),
.read(tx_read), .read(tx_read),
.rdata(tx_rdata), .rdata(tx_rdata),
.full(fifo_bus.tx_full), .full(fifo_bus.tx_full),
.almost_full(fifo_bus.tx_almost_full),
.write(fifo_bus.tx_write), .write(fifo_bus.tx_write),
.wdata(fifo_bus.tx_wdata), .wdata(fifo_bus.tx_wdata),
@ -140,9 +100,10 @@ module usb_ft1248 (
e_cmd cmd; e_cmd cmd;
e_cmd next_cmd; e_cmd next_cmd;
logic [3:0] phase; logic [3:0] phase;
logic rx_write;
logic last_rx_failed;
logic last_tx_failed; logic last_tx_failed;
logic reset_reply; logic reset_reply;
logic last_reset_status;
logic [4:0] modem_status_counter; logic [4:0] modem_status_counter;
logic write_modem_status_pending; logic write_modem_status_pending;
logic write_buffer_flush_pending; logic write_buffer_flush_pending;
@ -152,7 +113,9 @@ module usb_ft1248 (
cmd <= next_cmd; cmd <= next_cmd;
usb_scb.pwrsav <= !ft_pwrsav; usb_scb.pwrsav <= !ft_pwrsav;
usb_scb.reset_state <= last_reset_status; fifo_flush <= 1'b0;
rx_write_delayed <= rx_write;
phase <= {phase[2:0], phase[3]}; phase <= {phase[2:0], phase[3]};
if (state == STATE_IDLE) begin if (state == STATE_IDLE) begin
@ -160,25 +123,49 @@ module usb_ft1248 (
end end
if (reset) begin if (reset) begin
usb_scb.fifo_flush_busy <= 1'b0;
usb_scb.reset_state <= 1'b0;
last_rx_failed <= 1'b0;
last_tx_failed <= 1'b0; last_tx_failed <= 1'b0;
usb_scb.reset_pending <= 1'b0; reset_reply <= 1'b0;
last_reset_status <= 1'b0;
modem_status_counter <= 5'd0; modem_status_counter <= 5'd0;
write_modem_status_pending <= 1'b0; write_modem_status_pending <= 1'b1;
write_buffer_flush_pending <= 1'b0; write_buffer_flush_pending <= 1'b0;
end else begin end else begin
if (usb_scb.reset_ack) begin if (usb_scb.fifo_flush) begin
usb_scb.reset_pending <= 1'b0; usb_scb.fifo_flush_busy <= 1'b1;
end
if (usb_scb.reset_on_ack) begin
reset_reply <= 1'b1; reset_reply <= 1'b1;
write_modem_status_pending <= 1'b1; write_modem_status_pending <= 1'b1;
end end
if (usb_scb.reset_off_ack) begin
reset_reply <= 1'b0;
write_modem_status_pending <= 1'b1;
end
if (usb_scb.write_buffer_flush) begin if (usb_scb.write_buffer_flush) begin
write_buffer_flush_pending <= 1'b1; write_buffer_flush_pending <= 1'b1;
end end
if (state == STATE_IDLE) begin if (state == STATE_IDLE) begin
modem_status_counter <= modem_status_counter + 1'd1; modem_status_counter <= modem_status_counter + 1'd1;
if (usb_scb.fifo_flush_busy) begin
usb_scb.fifo_flush_busy <= 1'b0;
fifo_flush <= 1'b1;
last_rx_failed <= 1'b0;
last_tx_failed <= 1'b0;
end else if (last_rx_failed && !rx_full) begin
last_rx_failed <= 1'b0;
rx_write_delayed <= 1'b1;
end
end
if ((state == STATE_DATA) && (cmd == CMD_READ) && phase[3]) begin
rx_wdata <= ft_miosi_in;
last_rx_failed <= !ft_miso && rx_full;
end end
if ((state == STATE_DATA) && (cmd == CMD_WRITE) && phase[3]) begin if ((state == STATE_DATA) && (cmd == CMD_WRITE) && phase[3]) begin
@ -187,14 +174,7 @@ module usb_ft1248 (
if (!ft_miso && (state == STATE_DATA) && phase[3]) begin if (!ft_miso && (state == STATE_DATA) && phase[3]) begin
if (cmd == CMD_READ_MODEM_STATUS) begin if (cmd == CMD_READ_MODEM_STATUS) begin
last_reset_status <= ft_miosi_in[0]; usb_scb.reset_state <= ft_miosi_in[0];
if (!last_reset_status && ft_miosi_in[0]) begin
usb_scb.reset_pending <= 1'b1;
end
if (last_reset_status && !ft_miosi_in[0]) begin
reset_reply <= 1'b0;
write_modem_status_pending <= 1'b1;
end
end end
if (cmd == CMD_WRITE_MODEM_STATUS) begin if (cmd == CMD_WRITE_MODEM_STATUS) begin
write_modem_status_pending <= 1'b0; write_modem_status_pending <= 1'b0;
@ -252,8 +232,6 @@ module usb_ft1248 (
rx_write = 1'b0; rx_write = 1'b0;
tx_read = 1'b0; tx_read = 1'b0;
rx_wdata = ft_miosi_in;
if (!ft_miso && phase[3]) begin if (!ft_miso && phase[3]) begin
case (state) case (state)
STATE_STATUS: begin STATE_STATUS: begin
@ -263,13 +241,15 @@ module usb_ft1248 (
end end
STATE_DATA: begin STATE_DATA: begin
if (cmd == CMD_READ) begin if (cmd == CMD_READ && !rx_full) begin
rx_write = 1'b1; rx_write = 1'b1;
end end
if (cmd == CMD_WRITE && !tx_empty) begin if (cmd == CMD_WRITE && !tx_empty) begin
tx_read = 1'b1; tx_read = 1'b1;
end end
end end
default: begin end
endcase endcase
end end
end end
@ -283,7 +263,7 @@ module usb_ft1248 (
end else begin end else begin
case (state) case (state)
STATE_IDLE: begin STATE_IDLE: begin
if (ft_pwrsav) begin if (ft_pwrsav && !(usb_scb.fifo_flush || usb_scb.fifo_flush_busy || fifo_flush)) begin
if (write_modem_status_pending) begin if (write_modem_status_pending) begin
next_state = STATE_SELECT; next_state = STATE_SELECT;
next_cmd = CMD_WRITE_MODEM_STATUS; next_cmd = CMD_WRITE_MODEM_STATUS;
@ -330,7 +310,7 @@ module usb_ft1248 (
if (ft_miso) begin if (ft_miso) begin
next_state = STATE_DESELECT; next_state = STATE_DESELECT;
end else if (cmd == CMD_READ) begin end else if (cmd == CMD_READ) begin
if (rx_almost_full) begin if (rx_full) begin
next_state = STATE_DESELECT; next_state = STATE_DESELECT;
end end
end else if (cmd == CMD_WRITE) begin end else if (cmd == CMD_WRITE) begin

37
fw/rtl/usb/usb_scb.sv Normal file
View File

@ -0,0 +1,37 @@
interface usb_scb ();
logic fifo_flush;
logic fifo_flush_busy;
logic write_buffer_flush;
logic [10:0] rx_count;
logic [10:0] tx_count;
logic pwrsav;
logic reset_state;
logic reset_on_ack;
logic reset_off_ack;
modport controller (
output fifo_flush,
input fifo_flush_busy,
output write_buffer_flush,
input rx_count,
input tx_count,
input pwrsav,
input reset_state,
output reset_on_ack,
output reset_off_ack
);
modport usb (
input fifo_flush,
output fifo_flush_busy,
input write_buffer_flush,
output rx_count,
output tx_count,
output pwrsav,
output reset_state,
input reset_on_ack,
input reset_off_ack
);
endinterface

View File

@ -3,18 +3,19 @@ module fifo_8kb (
input reset, input reset,
output empty, output empty,
output almost_empty,
input read, input read,
output [7:0] rdata, output [7:0] rdata,
output full, output full,
output almost_full,
input write, input write,
input [7:0] wdata, input [7:0] wdata,
output logic [10:0] count output logic [10:0] count
); );
logic almost_empty;
logic almost_full;
fifo_8kb_lattice_generated fifo_8kb_lattice_generated_inst ( fifo_8kb_lattice_generated fifo_8kb_lattice_generated_inst (
.Data(wdata), .Data(wdata),
.WrClock(clk), .WrClock(clk),
@ -25,7 +26,7 @@ module fifo_8kb (
.RPReset(reset), .RPReset(reset),
.Q(rdata), .Q(rdata),
.Empty(empty), .Empty(empty),
.Full(full), .Full(full),
.AlmostEmpty(almost_empty), .AlmostEmpty(almost_empty),
.AlmostFull(almost_full) .AlmostFull(almost_full)
); );

View File

@ -1,7 +1,7 @@
/* Verilog netlist generated by SCUBA Diamond (64-bit) 3.12.1.454 */ /* Verilog netlist generated by SCUBA Diamond (64-bit) 3.13.0.56.2 */
/* Module Version: 5.7 */ /* Module Version: 5.7 */
/* C:\lscc\diamond\3.12\ispfpga\bin\nt64\scuba.exe -w -n pll_lattice_generated -lang verilog -synth synplify -arch xo2c00 -type pll -fin 50 -fclkop 100 -fclkop_tol 0.0 -fclkos 100 -fclkos_tol 0.0 -trimp 0 -phasep 0 -trimp_r -trims 0 -phases 90 -trims_r -phase_cntl STATIC -fb_mode 1 -lock */ /* C:\lscc\diamond\3.13\ispfpga\bin\nt64\scuba.exe -w -n pll_lattice_generated -lang verilog -synth synplify -arch xo2c00 -type pll -fin 50 -fclkop 100 -fclkop_tol 0.0 -fclkos 100 -fclkos_tol 0.0 -trimp 0 -phasep 0 -trimp_r -trims 0 -phases 270 -trims_r -phase_cntl STATIC -fb_mode 1 -lock */
/* Sat Mar 19 17:10:12 2022 */ /* Sun May 05 06:07:05 2024 */
`timescale 1 ns / 1 ps `timescale 1 ns / 1 ps
@ -29,8 +29,8 @@ module pll_lattice_generated (CLKI, CLKOP, CLKOS, LOCK)/* synthesis NGD_DRC_MASK
defparam PLLInst_0.CLKOS3_CPHASE = 0 ; defparam PLLInst_0.CLKOS3_CPHASE = 0 ;
defparam PLLInst_0.CLKOS2_FPHASE = 0 ; defparam PLLInst_0.CLKOS2_FPHASE = 0 ;
defparam PLLInst_0.CLKOS2_CPHASE = 0 ; defparam PLLInst_0.CLKOS2_CPHASE = 0 ;
defparam PLLInst_0.CLKOS_FPHASE = 2 ; defparam PLLInst_0.CLKOS_FPHASE = 6 ;
defparam PLLInst_0.CLKOS_CPHASE = 5 ; defparam PLLInst_0.CLKOS_CPHASE = 7 ;
defparam PLLInst_0.CLKOP_FPHASE = 0 ; defparam PLLInst_0.CLKOP_FPHASE = 0 ;
defparam PLLInst_0.CLKOP_CPHASE = 4 ; defparam PLLInst_0.CLKOP_CPHASE = 4 ;
defparam PLLInst_0.PLL_LOCK_MODE = 0 ; defparam PLLInst_0.PLL_LOCK_MODE = 0 ;

View File

@ -17,8 +17,8 @@ module pll (
); );
ODDRXE oddrxe_sdram_clk_inst ( ODDRXE oddrxe_sdram_clk_inst (
.D0(1'b0), .D0(1'b1),
.D1(1'b1), .D1(1'b0),
.SCLK(pll_sdram_clk), .SCLK(pll_sdram_clk),
.RST(1'b0), .RST(1'b0),
.Q(buf_sdram_clk) .Q(buf_sdram_clk)

1
fw/tests/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

35
fw/tests/Makefile Normal file
View File

@ -0,0 +1,35 @@
RTL_DIR = ../rtl
BENCHES_DIR = benches
MOCKS_DIR = mocks
BUILD_DIR = build
SRC_DIRS = \
$(RTL_DIR)/fifo \
$(RTL_DIR)/mcu \
$(RTL_DIR)/memory \
$(RTL_DIR)/n64 \
$(RTL_DIR)/sd \
$(RTL_DIR)/serv \
$(RTL_DIR)/usb \
$(RTL_DIR)/vendor \
$(RTL_DIR) \
$(MOCKS_DIR)/vendor \
$(MOCKS_DIR)
INC_DIRS = $(addprefix -I, $(SRC_DIRS))
TEST_FILES = $(shell find "./$(BENCHES_DIR)" -not -path "$(BUILD_DIR)/*" -type f -name "*_tb.sv")
TESTS = $(addprefix $(BUILD_DIR)/, $(basename $(TEST_FILES)))
VERILATOR_FLAGS = --binary --trace --timescale 10ns/1ns -j --quiet $(INC_DIRS)
$(BUILD_DIR)/%: %.sv
@echo "[VERILATOR] $<"
@mkdir -p $@.obj
@verilator $(VERILATOR_FLAGS) -Mdir $@.obj $< > /dev/null
@$@.obj/V$(notdir $@)
tests: $(TESTS)
clean:
@rm -rf ./$(BUILD_DIR)
.PHONY: tests

View File

@ -0,0 +1,126 @@
module memory_dma_tb;
logic clk;
logic reset;
dma_scb dma_scb ();
fifo_bus fifo_bus ();
mem_bus mem_bus ();
logic start;
logic stop;
logic direction;
logic byte_swap;
logic [26:0] starting_address;
logic [26:0] transfer_length;
logic flush;
logic rx_fill_enabled;
logic tx_drain_enabled;
memory_dma memory_dma (
.clk(clk),
.reset(reset),
.dma_scb(dma_scb),
.fifo_bus(fifo_bus),
.mem_bus(mem_bus)
);
dma_controller_mock dma_controller_mock (
.clk(clk),
.reset(reset),
.dma_scb(dma_scb),
.start(start),
.stop(stop),
.direction(direction),
.byte_swap(byte_swap),
.starting_address(starting_address),
.transfer_length(transfer_length)
);
fifo_bus_fifo_mock #(
.DEPTH(8),
.FILL_RATE(3),
.DRAIN_RATE(3)
) fifo_bus_fifo_mock (
.clk(clk),
.reset(reset),
.fifo_bus(fifo_bus),
.flush(flush),
.rx_fill_enabled(rx_fill_enabled),
.tx_drain_enabled(tx_drain_enabled)
);
memory_sdram_mock memory_sdram_mock (
.clk(clk),
.reset(reset),
.mem_bus(mem_bus)
);
initial begin
clk = 1'b0;
forever begin
clk = ~clk; #0.5;
end
end
initial begin
reset = 1'b0;
#10;
reset = 1'b1;
#10;
reset = 1'b0;
end
initial begin
$dumpfile("traces/memory_dma_tb.vcd");
#10000;
$dumpvars();
#100;
start = 1'b1;
direction = 1'b0;
byte_swap = 1'b0;
starting_address = 27'hFFF1;
transfer_length = 27'd64;
#1;
start = 1'b0;
#9;
tx_drain_enabled = 1'b1;
#490;
stop = 1'b1;
#1;
stop = 1'b0;
#165;
start = 1'b1;
direction = 1'b1;
#1;
start = 1'b0;
#9;
rx_fill_enabled = 1'b1;
#490;
stop = 1'b1;
#1;
stop = 1'b0;
#99;
$finish;
end
endmodule

View File

@ -0,0 +1,119 @@
module usb_ft1248_tb;
logic clk;
logic reset;
usb_scb usb_scb ();
fifo_bus fifo_bus ();
logic usb_pwrsav;
logic usb_clk;
logic usb_cs;
logic usb_miso;
logic [7:0] usb_miosi;
usb_ft1248 usb_ft1248 (
.clk(clk),
.reset(reset),
.usb_scb(usb_scb),
.fifo_bus(fifo_bus),
.usb_pwrsav(usb_pwrsav),
.usb_clk(usb_clk),
.usb_cs(usb_cs),
.usb_miso(usb_miso),
.usb_miosi(usb_miosi)
);
initial begin
clk = 1'b0;
forever begin
clk = ~clk; #0.5;
end
end
initial begin
reset = 1'b1;
#10;
reset = 1'b0;
end
initial begin
$dumpfile("traces/usb_ft1248_tb.vcd");
$dumpvars();
usb_pwrsav = 1'b1;
usb_miso = 1'b1;
#100;
fifo_bus.tx_write = 1'b1;
#100;
fifo_bus.tx_write = 1'b0;
#103;
usb_miso = 1'b0;
#80;
usb_scb.write_buffer_flush = 1'b1;
#1;
usb_scb.write_buffer_flush = 1'b0;
#20;
usb_miso = 1'b1;
#26;
usb_miso = 1'b0;
#4430;
usb_miso = 1'b1;
#13;
usb_miso = 1'b0;
#79;
fifo_bus.rx_read = 1'b1;
#1;
fifo_bus.rx_read = 1'b0;
#10;
fifo_bus.rx_read = 1'b1;
#1;
fifo_bus.rx_read = 1'b0;
#80;
fifo_bus.rx_read = 1'b1;
#1;
fifo_bus.rx_read = 1'b0;
#200;
usb_scb.reset_on_ack = 1'b1;
#1;
usb_scb.reset_on_ack = 1'b0;
#200;
usb_scb.reset_off_ack = 1'b1;
#1;
usb_scb.reset_off_ack = 1'b0;
#200;
usb_scb.fifo_flush = 1'b1;
#1;
usb_scb.fifo_flush = 1'b0;
#3000;
usb_scb.fifo_flush = 1'b1;
#1;
usb_scb.fifo_flush = 1'b0;
#6000;
$finish;
end
endmodule

22
fw/tests/docker_run.sh Executable file
View File

@ -0,0 +1,22 @@
#!/bin/bash
pushd $(dirname $0) > /dev/null
docker run \
-it \
--rm \
--user $(id -u):$(id -g) \
-v "$(pwd)":/work \
-v "$(pwd)/../rtl":/rtl \
-e CCACHE_DIR=/tmp/ccache \
--entrypoint /bin/bash \
verilator/verilator:latest \
-c "make -j"
BUILD_ERROR=$?
popd > /dev/null
if [ $BUILD_ERROR -ne 0 ]; then
exit -1
fi

View File

@ -0,0 +1,39 @@
module dma_controller_mock (
input clk,
input reset,
dma_scb.controller dma_scb,
input start,
input stop,
input direction,
input byte_swap,
input [26:0] starting_address,
input [26:0] transfer_length
);
always_ff @(posedge clk) begin
dma_scb.start <= 1'b0;
dma_scb.stop <= 1'b0;
if (reset) begin
dma_scb.direction <= 1'b0;
dma_scb.byte_swap <= 1'b0;
dma_scb.starting_address <= 27'd0;
dma_scb.transfer_length <= 27'd0;
end else begin
if (start) begin
dma_scb.start <= 1'b1;
dma_scb.direction <= direction;
dma_scb.byte_swap <= byte_swap;
dma_scb.starting_address <= starting_address;
dma_scb.transfer_length <= transfer_length;
end
if (stop) begin
dma_scb.stop <= 1'b1;
end
end
end
endmodule

View File

@ -0,0 +1,145 @@
module fifo_bus_fifo_mock #(
parameter int DEPTH = 1024,
parameter int FILL_RATE = 3,
parameter int DRAIN_RATE = 3
) (
input clk,
input reset,
fifo_bus.fifo fifo_bus,
input flush,
input rx_fill_enabled,
input tx_drain_enabled
);
localparam int PTR_BITS = $clog2(DEPTH);
// RX FIFO mock
logic rx_full;
logic rx_write;
logic [7:0] rx_wdata;
logic [PTR_BITS:0] rx_count;
fifo_mock #(
.DEPTH(DEPTH)
) fifo_rx (
.clk(clk),
.reset(reset),
.empty(fifo_bus.rx_empty),
.read(fifo_bus.rx_read),
.rdata(fifo_bus.rx_rdata),
.full(rx_full),
.write(rx_write),
.wdata(rx_wdata),
.count(rx_count)
);
localparam int FILL_BITS = $clog2(FILL_RATE);
logic [FILL_BITS:0] fill_counter;
logic rx_fill;
always_ff @(posedge clk) begin
rx_fill <= rx_fill_enabled;
end
generate;
if (FILL_RATE == 0) begin
always_comb begin
rx_write = rx_fill && !rx_full;
end
end else begin
always_comb begin
rx_write = rx_fill && !rx_full && (fill_counter == (FILL_BITS + 1)'(FILL_RATE));
end
always_ff @(posedge clk) begin
if (fill_counter < (FILL_BITS + 1)'(FILL_RATE)) begin
fill_counter <= fill_counter + (FILL_BITS + 1)'('d1);
end
if (reset) begin
fill_counter <= (FILL_BITS + 1)'('d0);
end else begin
if (rx_write) begin
fill_counter <= (FILL_BITS + 1)'('d0);
end
end
end
end
endgenerate
always_ff @(posedge clk) begin
if (reset) begin
rx_wdata <= 8'h01;
end else begin
if (rx_write) begin
rx_wdata <= rx_wdata + 8'h01;
end
end
end
// TX FIFO mock
logic tx_empty;
logic tx_read;
logic [7:0] tx_rdata;
logic [PTR_BITS:0] tx_count;
fifo_mock #(
.DEPTH(DEPTH)
) fifo_tx (
.clk(clk),
.reset(reset),
.empty(tx_empty),
.read(tx_read),
.rdata(tx_rdata),
.full(fifo_bus.tx_full),
.write(fifo_bus.tx_write),
.wdata(fifo_bus.tx_wdata),
.count(tx_count)
);
localparam int DRAIN_BITS = $clog2(DRAIN_RATE);
logic [DRAIN_BITS:0] drain_counter;
logic tx_drain;
always_ff @(posedge clk) begin
tx_drain <= tx_drain_enabled;
end
generate;
if (DRAIN_RATE == 0) begin
always_comb begin
tx_read = tx_drain && !tx_empty;
end
end else begin
always_comb begin
tx_read = tx_drain && !tx_empty && (drain_counter == (DRAIN_BITS + 1)'(DRAIN_RATE));
end
always_ff @(posedge clk) begin
if (drain_counter < (DRAIN_BITS + 1)'(DRAIN_RATE)) begin
drain_counter <= drain_counter + (DRAIN_BITS + 1)'('d1);
end
if (reset) begin
drain_counter <= (DRAIN_BITS + 1)'('d0);
end else begin
if (tx_read) begin
drain_counter <= (DRAIN_BITS + 1)'('d0);
end
end
end
end
endgenerate
endmodule

View File

@ -0,0 +1,49 @@
module fifo_mock #(
parameter int DEPTH = 1024,
localparam int PTR_BITS = $clog2(DEPTH)
) (
input clk,
input reset,
output logic empty,
input read,
output [7:0] rdata,
output logic full,
input write,
input [7:0] wdata,
output logic [PTR_BITS:0] count
);
logic [7:0] fifo_mem [0:(DEPTH - 1)];
logic [(PTR_BITS - 1):0] fifo_rptr;
logic [(PTR_BITS - 1):0] fifo_wptr;
always_comb begin
full = count >= (PTR_BITS + 1)'(DEPTH);
empty = count == (PTR_BITS + 1)'('d0);
end
always_ff @(posedge clk) begin
if (read) begin
rdata <= fifo_mem[fifo_rptr];
fifo_rptr <= fifo_rptr + PTR_BITS'('d1);
count <= count - (PTR_BITS + 1)'('d1);
end
if (write) begin
fifo_mem[fifo_wptr] <= wdata;
fifo_wptr <= fifo_wptr + PTR_BITS'('d1);
count <= count + (PTR_BITS + 1)'('d1);
end
if (read && write) begin
count <= count;
end
if (reset) begin
count <= (PTR_BITS + 1)'('d0);
fifo_rptr <= PTR_BITS'('d0);
fifo_wptr <= PTR_BITS'('d0);
end
end
endmodule

View File

@ -0,0 +1,70 @@
module memory_sdram_mock (
input clk,
input reset,
mem_bus.memory mem_bus
);
logic sdram_cs;
logic sdram_ras;
logic sdram_cas;
logic sdram_we;
logic [1:0] sdram_ba;
logic [12:0] sdram_a;
logic [1:0] sdram_dqm;
logic [15:0] sdram_dq;
memory_sdram memory_sdram_inst (
.clk(clk),
.reset(reset),
.mem_bus(mem_bus),
.sdram_cs(sdram_cs),
.sdram_ras(sdram_ras),
.sdram_cas(sdram_cas),
.sdram_we(sdram_we),
.sdram_ba(sdram_ba),
.sdram_a(sdram_a),
.sdram_dqm(sdram_dqm),
.sdram_dq(sdram_dq)
);
logic [1:0] cas_delay;
logic [15:0] data_from_sdram;
logic [15:0] data_to_sdram;
logic [15:0] sdram_dq_driven;
assign sdram_dq = sdram_dq_driven;
always_ff @(posedge clk) begin
if (reset) begin
cas_delay <= 2'b00;
data_from_sdram <= 16'h0102;
data_to_sdram <= 16'hFFFF;
end else begin
cas_delay <= {cas_delay[0], 1'b0};
if ({sdram_cs, sdram_ras, sdram_cas, sdram_we} == 4'b0101) begin
cas_delay[0] <= 1'b1;
end
if (cas_delay[1]) begin
data_from_sdram <= data_from_sdram + 16'h0202;
end
if ({sdram_cs, sdram_ras, sdram_cas, sdram_we} == 4'b0100) begin
if (!sdram_dqm[0]) data_to_sdram[7:0] <= sdram_dq[7:0];
if (!sdram_dqm[1]) data_to_sdram[15:8] <= sdram_dq[15:8];
end
end
end
always_comb begin
sdram_dq_driven = 16'hXXXX;
if (cas_delay[1]) begin
sdram_dq_driven = data_from_sdram;
end
end
endmodule

33
fw/tests/mocks/vendor/fifo_8kb.sv vendored Normal file
View File

@ -0,0 +1,33 @@
module fifo_8kb (
input clk,
input reset,
output empty,
input read,
output [7:0] rdata,
output full,
input write,
input [7:0] wdata,
output logic [10:0] count
);
fifo_mock #(
.DEPTH(1024)
) fifo_8kb (
.clk(clk),
.reset(reset),
.empty(empty),
.read(read),
.rdata(rdata),
.full(full),
.write(write),
.wdata(wdata),
.count(count)
);
endmodule

2
fw/tests/traces/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.vcd
*.gtkw

2
hw/pcb/.gitignore vendored
View File

@ -1,5 +1,7 @@
*.lck
**/*-backups **/*-backups
**/*-bak **/*-bak
**/*.3dshapes
**/*.csv **/*.csv
**/*.gerbers **/*.gerbers
**/*.kicad_prl **/*.kicad_prl

289
hw/pcb/LICENSE Normal file
View File

@ -0,0 +1,289 @@
CERN Open Hardware Licence Version 2 - Strongly Reciprocal
Preamble
CERN has developed this licence to promote collaboration among
hardware designers and to provide a legal tool which supports the
freedom to use, study, modify, share and distribute hardware designs
and products based on those designs. Version 2 of the CERN Open
Hardware Licence comes in three variants: CERN-OHL-P (permissive); and
two reciprocal licences: CERN-OHL-W (weakly reciprocal) and this
licence, CERN-OHL-S (strongly reciprocal).
The CERN-OHL-S is copyright CERN 2020. Anyone is welcome to use it, in
unmodified form only.
Use of this Licence does not imply any endorsement by CERN of any
Licensor or their designs nor does it imply any involvement by CERN in
their development.
1 Definitions
1.1 'Licence' means this CERN-OHL-S.
1.2 'Compatible Licence' means
a) any earlier version of the CERN Open Hardware licence, or
b) any version of the CERN-OHL-S, or
c) any licence which permits You to treat the Source to which
it applies as licensed under CERN-OHL-S provided that on
Conveyance of any such Source, or any associated Product You
treat the Source in question as being licensed under
CERN-OHL-S.
1.3 'Source' means information such as design materials or digital
code which can be applied to Make or test a Product or to
prepare a Product for use, Conveyance or sale, regardless of its
medium or how it is expressed. It may include Notices.
1.4 'Covered Source' means Source that is explicitly made available
under this Licence.
1.5 'Product' means any device, component, work or physical object,
whether in finished or intermediate form, arising from the use,
application or processing of Covered Source.
1.6 'Make' means to create or configure something, whether by
manufacture, assembly, compiling, loading or applying Covered
Source or another Product or otherwise.
1.7 'Available Component' means any part, sub-assembly, library or
code which:
a) is licensed to You as Complete Source under a Compatible
Licence; or
b) is available, at the time a Product or the Source containing
it is first Conveyed, to You and any other prospective
licensees
i) as a physical part with sufficient rights and
information (including any configuration and
programming files and information about its
characteristics and interfaces) to enable it either to
be Made itself, or to be sourced and used to Make the
Product; or
ii) as part of the normal distribution of a tool used to
design or Make the Product.
1.8 'Complete Source' means the set of all Source necessary to Make
a Product, in the preferred form for making modifications,
including necessary installation and interfacing information
both for the Product, and for any included Available Components.
If the format is proprietary, it must also be made available in
a format (if the proprietary tool can create it) which is
viewable with a tool available to potential licensees and
licensed under a licence approved by the Free Software
Foundation or the Open Source Initiative. Complete Source need
not include the Source of any Available Component, provided that
You include in the Complete Source sufficient information to
enable a recipient to Make or source and use the Available
Component to Make the Product.
1.9 'Source Location' means a location where a Licensor has placed
Covered Source, and which that Licensor reasonably believes will
remain easily accessible for at least three years for anyone to
obtain a digital copy.
1.10 'Notice' means copyright, acknowledgement and trademark notices,
Source Location references, modification notices (subsection
3.3(b)) and all notices that refer to this Licence and to the
disclaimer of warranties that are included in the Covered
Source.
1.11 'Licensee' or 'You' means any person exercising rights under
this Licence.
1.12 'Licensor' means a natural or legal person who creates or
modifies Covered Source. A person may be a Licensee and a
Licensor at the same time.
1.13 'Convey' means to communicate to the public or distribute.
2 Applicability
2.1 This Licence governs the use, copying, modification, Conveying
of Covered Source and Products, and the Making of Products. By
exercising any right granted under this Licence, You irrevocably
accept these terms and conditions.
2.2 This Licence is granted by the Licensor directly to You, and
shall apply worldwide and without limitation in time.
2.3 You shall not attempt to restrict by contract or otherwise the
rights granted under this Licence to other Licensees.
2.4 This Licence is not intended to restrict fair use, fair dealing,
or any other similar right.
3 Copying, Modifying and Conveying Covered Source
3.1 You may copy and Convey verbatim copies of Covered Source, in
any medium, provided You retain all Notices.
3.2 You may modify Covered Source, other than Notices, provided that
You irrevocably undertake to make that modified Covered Source
available from a Source Location should You Convey a Product in
circumstances where the recipient does not otherwise receive a
copy of the modified Covered Source. In each case subsection 3.3
shall apply.
You may only delete Notices if they are no longer applicable to
the corresponding Covered Source as modified by You and You may
add additional Notices applicable to Your modifications.
Including Covered Source in a larger work is modifying the
Covered Source, and the larger work becomes modified Covered
Source.
3.3 You may Convey modified Covered Source (with the effect that You
shall also become a Licensor) provided that You:
a) retain Notices as required in subsection 3.2;
b) add a Notice to the modified Covered Source stating that You
have modified it, with the date and brief description of how
You have modified it;
c) add a Source Location Notice for the modified Covered Source
if You Convey in circumstances where the recipient does not
otherwise receive a copy of the modified Covered Source; and
d) license the modified Covered Source under the terms and
conditions of this Licence (or, as set out in subsection
8.3, a later version, if permitted by the licence of the
original Covered Source). Such modified Covered Source must
be licensed as a whole, but excluding Available Components
contained in it, which remain licensed under their own
applicable licences.
4 Making and Conveying Products
You may Make Products, and/or Convey them, provided that You either
provide each recipient with a copy of the Complete Source or ensure
that each recipient is notified of the Source Location of the Complete
Source. That Complete Source is Covered Source, and You must
accordingly satisfy Your obligations set out in subsection 3.3. If
specified in a Notice, the Product must visibly and securely display
the Source Location on it or its packaging or documentation in the
manner specified in that Notice.
5 Research and Development
You may Convey Covered Source, modified Covered Source or Products to
a legal entity carrying out development, testing or quality assurance
work on Your behalf provided that the work is performed on terms which
prevent the entity from both using the Source or Products for its own
internal purposes and Conveying the Source or Products or any
modifications to them to any person other than You. Any modifications
made by the entity shall be deemed to be made by You pursuant to
subsection 3.2.
6 DISCLAIMER AND LIABILITY
6.1 DISCLAIMER OF WARRANTY -- The Covered Source and any Products
are provided 'as is' and any express or implied warranties,
including, but not limited to, implied warranties of
merchantability, of satisfactory quality, non-infringement of
third party rights, and fitness for a particular purpose or use
are disclaimed in respect of any Source or Product to the
maximum extent permitted by law. The Licensor makes no
representation that any Source or Product does not or will not
infringe any patent, copyright, trade secret or other
proprietary right. The entire risk as to the use, quality, and
performance of any Source or Product shall be with You and not
the Licensor. This disclaimer of warranty is an essential part
of this Licence and a condition for the grant of any rights
granted under this Licence.
6.2 EXCLUSION AND LIMITATION OF LIABILITY -- The Licensor shall, to
the maximum extent permitted by law, have no liability for
direct, indirect, special, incidental, consequential, exemplary,
punitive or other damages of any character including, without
limitation, procurement of substitute goods or services, loss of
use, data or profits, or business interruption, however caused
and on any theory of contract, warranty, tort (including
negligence), product liability or otherwise, arising in any way
in relation to the Covered Source, modified Covered Source
and/or the Making or Conveyance of a Product, even if advised of
the possibility of such damages, and You shall hold the
Licensor(s) free and harmless from any liability, costs,
damages, fees and expenses, including claims by third parties,
in relation to such use.
7 Patents
7.1 Subject to the terms and conditions of this Licence, each
Licensor hereby grants to You a perpetual, worldwide,
non-exclusive, no-charge, royalty-free, irrevocable (except as
stated in subsections 7.2 and 8.4) patent licence to Make, have
Made, use, offer to sell, sell, import, and otherwise transfer
the Covered Source and Products, where such licence applies only
to those patent claims licensable by such Licensor that are
necessarily infringed by exercising rights under the Covered
Source as Conveyed by that Licensor.
7.2 If You institute patent litigation against any entity (including
a cross-claim or counterclaim in a lawsuit) alleging that the
Covered Source or a Product constitutes direct or contributory
patent infringement, or You seek any declaration that a patent
licensed to You under this Licence is invalid or unenforceable
then any rights granted to You under this Licence shall
terminate as of the date such process is initiated.
8 General
8.1 If any provisions of this Licence are or subsequently become
invalid or unenforceable for any reason, the remaining
provisions shall remain effective.
8.2 You shall not use any of the name (including acronyms and
abbreviations), image, or logo by which the Licensor or CERN is
known, except where needed to comply with section 3, or where
the use is otherwise allowed by law. Any such permitted use
shall be factual and shall not be made so as to suggest any kind
of endorsement or implication of involvement by the Licensor or
its personnel.
8.3 CERN may publish updated versions and variants of this Licence
which it considers to be in the spirit of this version, but may
differ in detail to address new problems or concerns. New
versions will be published with a unique version number and a
variant identifier specifying the variant. If the Licensor has
specified that a given variant applies to the Covered Source
without specifying a version, You may treat that Covered Source
as being released under any version of the CERN-OHL with that
variant. If no variant is specified, the Covered Source shall be
treated as being released under CERN-OHL-S. The Licensor may
also specify that the Covered Source is subject to a specific
version of the CERN-OHL or any later version in which case You
may apply this or any later version of CERN-OHL with the same
variant identifier published by CERN.
8.4 This Licence shall terminate with immediate effect if You fail
to comply with any of its terms and conditions.
8.5 However, if You cease all breaches of this Licence, then Your
Licence from any Licensor is reinstated unless such Licensor has
terminated this Licence by giving You, while You remain in
breach, a notice specifying the breach and requiring You to cure
it within 30 days, and You have failed to come into compliance
in all material respects by the end of the 30 day period. Should
You repeat the breach after receipt of a cure notice and
subsequent reinstatement, this Licence will terminate
immediately and permanently. Section 6 shall continue to apply
after any termination.
8.6 This Licence shall not be enforceable except by a Licensor
acting as such, and third party beneficiary rights are
specifically excluded.

31
hw/pcb/ibom.config.ini Normal file
View File

@ -0,0 +1,31 @@
[html_defaults]
dark_mode=1
show_pads=1
show_fabrication=0
show_silkscreen=1
redraw_on_drag=1
highlight_pin1=selected
board_rotation=0
offset_back_rotation=0
checkboxes=Sourced,Placed
bom_view=left-right
layer_view=FB
compression=1
open_browser=0
[general]
bom_dest_dir=.
bom_name_format=%f_bom
component_sort_order=C,R,L,D,U,Y,X,F,SW,A,~,HS,CNN,J,P,NT,MH
component_blacklist=
blacklist_virtual=1
blacklist_empty_val=0
include_tracks=0
include_nets=0
[fields]
show_fields=Value,Footprint,Notes
group_fields=Value,Footprint
normalize_field_case=0
board_variant_field=
board_variant_whitelist=
board_variant_blacklist=
dnp_field=kicad_dnp

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -3,14 +3,17 @@
"3dviewports": [], "3dviewports": [],
"design_settings": { "design_settings": {
"defaults": { "defaults": {
"board_outline_line_width": 0.09999999999999999, "apply_defaults_to_fp_fields": false,
"copper_line_width": 0.19999999999999998, "apply_defaults_to_fp_shapes": false,
"apply_defaults_to_fp_text": false,
"board_outline_line_width": 0.1,
"copper_line_width": 0.2,
"copper_text_italic": false, "copper_text_italic": false,
"copper_text_size_h": 1.5, "copper_text_size_h": 1.5,
"copper_text_size_v": 1.5, "copper_text_size_v": 1.5,
"copper_text_thickness": 0.3, "copper_text_thickness": 0.3,
"copper_text_upright": false, "copper_text_upright": false,
"courtyard_line_width": 0.049999999999999996, "courtyard_line_width": 0.05,
"dimension_precision": 4, "dimension_precision": 4,
"dimension_units": 3, "dimension_units": 3,
"dimensions": { "dimensions": {
@ -21,7 +24,7 @@
"text_position": 0, "text_position": 0,
"units_format": 1 "units_format": 1
}, },
"fab_line_width": 0.09999999999999999, "fab_line_width": 0.1,
"fab_text_italic": false, "fab_text_italic": false,
"fab_text_size_h": 1.0, "fab_text_size_h": 1.0,
"fab_text_size_v": 1.0, "fab_text_size_v": 1.0,
@ -34,9 +37,9 @@
"other_text_thickness": 0.15, "other_text_thickness": 0.15,
"other_text_upright": false, "other_text_upright": false,
"pads": { "pads": {
"drill": 0.762, "drill": 2.5,
"height": 1.524, "height": 2.5,
"width": 1.524 "width": 2.5
}, },
"silk_line_width": 0.15, "silk_line_width": 0.15,
"silk_text_italic": false, "silk_text_italic": false,
@ -63,20 +66,28 @@
"rule_severities": { "rule_severities": {
"annular_width": "error", "annular_width": "error",
"clearance": "error", "clearance": "error",
"connection_width": "warning",
"copper_edge_clearance": "error", "copper_edge_clearance": "error",
"copper_sliver": "warning",
"courtyards_overlap": "error", "courtyards_overlap": "error",
"diff_pair_gap_out_of_range": "error", "diff_pair_gap_out_of_range": "error",
"diff_pair_uncoupled_length_too_long": "error", "diff_pair_uncoupled_length_too_long": "error",
"drill_out_of_range": "error", "drill_out_of_range": "error",
"duplicate_footprints": "warning", "duplicate_footprints": "warning",
"extra_footprint": "warning", "extra_footprint": "warning",
"footprint": "error",
"footprint_symbol_mismatch": "warning",
"footprint_type_mismatch": "error", "footprint_type_mismatch": "error",
"hole_clearance": "error", "hole_clearance": "error",
"hole_near_hole": "error", "hole_near_hole": "error",
"holes_co_located": "warning",
"invalid_outline": "error", "invalid_outline": "error",
"isolated_copper": "warning",
"item_on_disabled_layer": "error", "item_on_disabled_layer": "error",
"items_not_allowed": "error", "items_not_allowed": "error",
"length_out_of_range": "error", "length_out_of_range": "error",
"lib_footprint_issues": "warning",
"lib_footprint_mismatch": "warning",
"malformed_courtyard": "error", "malformed_courtyard": "error",
"microvia_drill_out_of_range": "error", "microvia_drill_out_of_range": "error",
"missing_courtyard": "ignore", "missing_courtyard": "ignore",
@ -86,9 +97,14 @@
"padstack": "error", "padstack": "error",
"pth_inside_courtyard": "ignore", "pth_inside_courtyard": "ignore",
"shorting_items": "error", "shorting_items": "error",
"silk_edge_clearance": "warning",
"silk_over_copper": "warning", "silk_over_copper": "warning",
"silk_overlap": "warning", "silk_overlap": "warning",
"skew_out_of_range": "error", "skew_out_of_range": "error",
"solder_mask_bridge": "ignore",
"starved_thermal": "error",
"text_height": "warning",
"text_thickness": "warning",
"through_hole_pad_without_hole": "error", "through_hole_pad_without_hole": "error",
"too_many_vias": "error", "too_many_vias": "error",
"track_dangling": "warning", "track_dangling": "warning",
@ -97,7 +113,6 @@
"unconnected_items": "error", "unconnected_items": "error",
"unresolved_variable": "error", "unresolved_variable": "error",
"via_dangling": "warning", "via_dangling": "warning",
"zone_has_empty_net": "error",
"zones_intersect": "error" "zones_intersect": "error"
}, },
"rules": { "rules": {
@ -105,25 +120,99 @@
"allow_microvias": false, "allow_microvias": false,
"max_error": 0.005, "max_error": 0.005,
"min_clearance": 0.127, "min_clearance": 0.127,
"min_copper_edge_clearance": 0.19999999999999998, "min_connection": 0.0,
"min_copper_edge_clearance": 0.2,
"min_hole_clearance": 0.254, "min_hole_clearance": 0.254,
"min_hole_to_hole": 0.5, "min_hole_to_hole": 0.5,
"min_microvia_diameter": 0.19999999999999998, "min_microvia_diameter": 0.2,
"min_microvia_drill": 0.09999999999999999, "min_microvia_drill": 0.1,
"min_resolved_spokes": 2,
"min_silk_clearance": 0.0, "min_silk_clearance": 0.0,
"min_text_height": 0.8,
"min_text_thickness": 0.08,
"min_through_hole_diameter": 0.3, "min_through_hole_diameter": 0.3,
"min_track_width": 0.127, "min_track_width": 0.127,
"min_via_annular_width": 0.13, "min_via_annular_width": 0.13,
"min_via_diameter": 0.6, "min_via_diameter": 0.6,
"solder_mask_clearance": 0.0, "solder_mask_clearance": 0.0,
"solder_mask_min_width": 0.0, "solder_mask_min_width": 0.0,
"solder_mask_to_copper_clearance": 0.0,
"use_height_for_length_calcs": true "use_height_for_length_calcs": true
}, },
"teardrop_options": [
{
"td_onpadsmd": true,
"td_onroundshapesonly": false,
"td_ontrackend": false,
"td_onviapad": true
}
],
"teardrop_parameters": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [ "track_widths": [
0.0, 0.0,
0.508, 0.508,
0.762 0.762
], ],
"tuning_pattern_settings": {
"diff_pair_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 1.0
},
"diff_pair_skew_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
},
"single_track_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
}
},
"via_dimensions": [ "via_dimensions": [
{ {
"diameter": 0.0, "diameter": 0.0,
@ -133,6 +222,13 @@
"zones_allow_external_fillets": false, "zones_allow_external_fillets": false,
"zones_use_no_outline": true "zones_use_no_outline": true
}, },
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"layer_presets": [], "layer_presets": [],
"viewports": [] "viewports": []
}, },
@ -391,14 +487,94 @@
"gencad": "", "gencad": "",
"idf": "", "idf": "",
"netlist": "", "netlist": "",
"plot": "sc64v2.gerbers/",
"pos_files": "sc64v2.gerbers/",
"specctra_dsn": "", "specctra_dsn": "",
"step": "", "step": "",
"svg": "",
"vrml": "" "vrml": ""
}, },
"page_layout_descr_file": "" "page_layout_descr_file": ""
}, },
"schematic": { "schematic": {
"annotate_start_num": 0, "annotate_start_num": 0,
"bom_export_filename": "",
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",
"keep_line_breaks": false,
"keep_tabs": false,
"name": "CSV",
"ref_delimiter": ",",
"ref_range_delimiter": "",
"string_delimiter": "\""
},
"bom_presets": [],
"bom_settings": {
"exclude_dnp": false,
"fields_ordered": [
{
"group_by": false,
"label": "Reference",
"name": "Reference",
"show": true
},
{
"group_by": true,
"label": "Value",
"name": "Value",
"show": true
},
{
"group_by": false,
"label": "Datasheet",
"name": "Datasheet",
"show": true
},
{
"group_by": false,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": true
},
{
"group_by": true,
"label": "DNP",
"name": "${DNP}",
"show": true
},
{
"group_by": false,
"label": "#",
"name": "${ITEM_NUMBER}",
"show": false
},
{
"group_by": false,
"label": "Notes",
"name": "Notes",
"show": true
},
{
"group_by": false,
"label": "Description",
"name": "Description",
"show": false
}
],
"filter_string": "",
"group_symbols": true,
"name": "",
"sort_asc": true,
"sort_field": "Reference"
},
"connection_grid_size": 50.0,
"drawing": { "drawing": {
"dashed_lines_dash_length_ratio": 12.0, "dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0, "dashed_lines_gap_length_ratio": 3.0,
@ -412,6 +588,11 @@
"intersheets_ref_suffix": "", "intersheets_ref_suffix": "",
"junction_size_choice": 3, "junction_size_choice": 3,
"label_size_ratio": 0.375, "label_size_ratio": 0.375,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0, "pin_symbol_size": 25.0,
"text_offset_ratio": 0.15 "text_offset_ratio": 0.15
}, },
@ -437,6 +618,7 @@
"spice_external_command": "spice \"%I\"", "spice_external_command": "spice \"%I\"",
"spice_model_current_sheet_as_root": true, "spice_model_current_sheet_as_root": true,
"spice_save_all_currents": false, "spice_save_all_currents": false,
"spice_save_all_dissipations": false,
"spice_save_all_voltages": false, "spice_save_all_voltages": false,
"subpart_first_id": 65, "subpart_first_id": 65,
"subpart_id_separator": 0 "subpart_id_separator": 0
@ -444,7 +626,7 @@
"sheets": [ "sheets": [
[ [
"e63e39d7-6ac0-4ffd-8aa3-1841a4541b55", "e63e39d7-6ac0-4ffd-8aa3-1841a4541b55",
"" "Root"
] ]
], ],
"text_variables": {} "text_variables": {}

File diff suppressed because it is too large Load Diff

4669
hw/pcb/sc64v2_bom.html generated Normal file

File diff suppressed because one or more lines are too long

12
hw/shell/README.md Normal file
View File

@ -0,0 +1,12 @@
# SummerCart64 shell design files
This folder contains several different designs of the SummerCart64 shell and button
`./` folder contains original design made by [Mateusz Faderewski 'Polprzewodnikowy'](https://github.com/Polprzewodnikowy) in the Autodesk Fusion 360 and exported `.stl` files ready for 3D print.
`./injection mold` folder contains adapted design for injection molding process by [Mena 'Pheeeeenom'](https://github.com/Pheeeeenom).
`./button` folder contains designs for the button on the back of the SummerCart64. Designs are:
- `sc64_shell_button_b3fs-101x.stl` - for the Omron B3FS-101x button (by [Mason Stooksbury](https://github.com/MasonStooksbury))
- `sc64_shell_button_b3fs-105x.step` - for the Omron B3FS-105x button (by [Mena 'Pheeeeenom'](https://github.com/Pheeeeenom))
- `sc64_shell_button_b3fs-105x_with_end_stop.step` - for the Omron B3FS-105x button but with additional flange to prevent it from coming out of the shell (by [Mena 'Pheeeeenom'](https://github.com/Pheeeeenom))

Binary file not shown.

View File

@ -0,0 +1,600 @@
ISO-10303-21;
HEADER;
/* Generated by software containing ST-Developer
* from STEP Tools, Inc. (www.steptools.com)
*/
FILE_DESCRIPTION(
/* description */ (''),
/* implementation_level */ '2;1');
FILE_NAME(
/* name */ 'sc64_shell_button v3.step',
/* time_stamp */ '2024-01-03T20:51:50-06:00',
/* author */ (''),
/* organization */ (''),
/* preprocessor_version */ 'ST-DEVELOPER v20',
/* originating_system */ 'Autodesk Translation Framework v12.14.0.127',
/* authorisation */ '');
FILE_SCHEMA (('AUTOMOTIVE_DESIGN { 1 0 10303 214 3 1 1 }'));
ENDSEC;
DATA;
#10=MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION('',(#13),#504);
#11=SHAPE_REPRESENTATION_RELATIONSHIP('SRR','None',#510,#12);
#12=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#14),#503);
#13=STYLED_ITEM('',(#522),#14);
#14=MANIFOLD_SOLID_BREP('ButtonCap',#299);
#15=CYLINDRICAL_SURFACE('',#320,3.);
#16=TOROIDAL_SURFACE('',#316,2.8,0.2);
#17=ELLIPSE('',#309,57.3219678278922,3.);
#18=ELLIPSE('',#311,57.3219678278922,3.);
#19=ELLIPSE('',#315,57.3219678278922,3.);
#20=ELLIPSE('',#322,57.3219678278922,3.);
#21=CIRCLE('',#307,3.);
#22=CIRCLE('',#313,3.);
#23=CIRCLE('',#317,2.8);
#24=CIRCLE('',#318,0.2);
#25=CIRCLE('',#319,3.);
#26=CIRCLE('',#321,3.);
#27=CIRCLE('',#323,3.);
#28=CIRCLE('',#324,3.);
#29=FACE_OUTER_BOUND('',#45,.T.);
#30=FACE_OUTER_BOUND('',#46,.T.);
#31=FACE_OUTER_BOUND('',#47,.T.);
#32=FACE_OUTER_BOUND('',#48,.T.);
#33=FACE_OUTER_BOUND('',#49,.T.);
#34=FACE_OUTER_BOUND('',#50,.T.);
#35=FACE_OUTER_BOUND('',#51,.T.);
#36=FACE_OUTER_BOUND('',#52,.T.);
#37=FACE_OUTER_BOUND('',#53,.T.);
#38=FACE_OUTER_BOUND('',#54,.T.);
#39=FACE_OUTER_BOUND('',#55,.T.);
#40=FACE_OUTER_BOUND('',#56,.T.);
#41=FACE_OUTER_BOUND('',#57,.T.);
#42=FACE_OUTER_BOUND('',#58,.T.);
#43=FACE_OUTER_BOUND('',#59,.T.);
#44=FACE_OUTER_BOUND('',#60,.T.);
#45=EDGE_LOOP('',(#187,#188,#189,#190));
#46=EDGE_LOOP('',(#191,#192,#193,#194,#195,#196,#197,#198));
#47=EDGE_LOOP('',(#199,#200,#201,#202));
#48=EDGE_LOOP('',(#203,#204,#205,#206,#207,#208,#209,#210));
#49=EDGE_LOOP('',(#211,#212,#213,#214));
#50=EDGE_LOOP('',(#215,#216,#217,#218,#219,#220));
#51=EDGE_LOOP('',(#221,#222,#223,#224));
#52=EDGE_LOOP('',(#225,#226,#227,#228));
#53=EDGE_LOOP('',(#229,#230,#231,#232));
#54=EDGE_LOOP('',(#233,#234,#235,#236));
#55=EDGE_LOOP('',(#237,#238,#239,#240));
#56=EDGE_LOOP('',(#241,#242,#243,#244,#245,#246,#247,#248,#249,#250,#251,
#252));
#57=EDGE_LOOP('',(#253));
#58=EDGE_LOOP('',(#254,#255,#256,#257));
#59=EDGE_LOOP('',(#258,#259,#260,#261));
#60=EDGE_LOOP('',(#262,#263,#264,#265,#266,#267,#268));
#61=LINE('',#420,#90);
#62=LINE('',#422,#91);
#63=LINE('',#424,#92);
#64=LINE('',#425,#93);
#65=LINE('',#429,#94);
#66=LINE('',#431,#95);
#67=LINE('',#432,#96);
#68=LINE('',#434,#97);
#69=LINE('',#436,#98);
#70=LINE('',#438,#99);
#71=LINE('',#439,#100);
#72=LINE('',#442,#101);
#73=LINE('',#443,#102);
#74=LINE('',#447,#103);
#75=LINE('',#449,#104);
#76=LINE('',#451,#105);
#77=LINE('',#453,#106);
#78=LINE('',#454,#107);
#79=LINE('',#455,#108);
#80=LINE('',#457,#109);
#81=LINE('',#460,#110);
#82=LINE('',#463,#111);
#83=LINE('',#466,#112);
#84=LINE('',#471,#113);
#85=LINE('',#474,#114);
#86=LINE('',#478,#115);
#87=LINE('',#488,#116);
#88=LINE('',#497,#117);
#89=LINE('',#498,#118);
#90=VECTOR('',#333,10.);
#91=VECTOR('',#334,10.);
#92=VECTOR('',#335,10.);
#93=VECTOR('',#336,10.);
#94=VECTOR('',#339,10.);
#95=VECTOR('',#340,10.);
#96=VECTOR('',#341,10.);
#97=VECTOR('',#342,10.);
#98=VECTOR('',#343,10.);
#99=VECTOR('',#344,10.);
#100=VECTOR('',#345,10.);
#101=VECTOR('',#348,10.);
#102=VECTOR('',#349,10.);
#103=VECTOR('',#352,10.);
#104=VECTOR('',#353,10.);
#105=VECTOR('',#354,10.);
#106=VECTOR('',#355,10.);
#107=VECTOR('',#356,10.);
#108=VECTOR('',#357,10.);
#109=VECTOR('',#360,10.);
#110=VECTOR('',#363,10.);
#111=VECTOR('',#366,10.);
#112=VECTOR('',#369,10.);
#113=VECTOR('',#376,10.);
#114=VECTOR('',#379,10.);
#115=VECTOR('',#384,10.);
#116=VECTOR('',#397,3.);
#117=VECTOR('',#410,10.);
#118=VECTOR('',#411,10.);
#119=VERTEX_POINT('',#418);
#120=VERTEX_POINT('',#419);
#121=VERTEX_POINT('',#421);
#122=VERTEX_POINT('',#423);
#123=VERTEX_POINT('',#427);
#124=VERTEX_POINT('',#428);
#125=VERTEX_POINT('',#430);
#126=VERTEX_POINT('',#433);
#127=VERTEX_POINT('',#435);
#128=VERTEX_POINT('',#437);
#129=VERTEX_POINT('',#441);
#130=VERTEX_POINT('',#445);
#131=VERTEX_POINT('',#446);
#132=VERTEX_POINT('',#448);
#133=VERTEX_POINT('',#450);
#134=VERTEX_POINT('',#452);
#135=VERTEX_POINT('',#459);
#136=VERTEX_POINT('',#461);
#137=VERTEX_POINT('',#465);
#138=VERTEX_POINT('',#469);
#139=VERTEX_POINT('',#473);
#140=VERTEX_POINT('',#477);
#141=VERTEX_POINT('',#481);
#142=VERTEX_POINT('',#483);
#143=VERTEX_POINT('',#487);
#144=VERTEX_POINT('',#489);
#145=VERTEX_POINT('',#491);
#146=EDGE_CURVE('',#119,#120,#61,.T.);
#147=EDGE_CURVE('',#121,#119,#62,.T.);
#148=EDGE_CURVE('',#122,#121,#63,.T.);
#149=EDGE_CURVE('',#120,#122,#64,.T.);
#150=EDGE_CURVE('',#123,#124,#65,.T.);
#151=EDGE_CURVE('',#125,#123,#66,.T.);
#152=EDGE_CURVE('',#125,#119,#67,.T.);
#153=EDGE_CURVE('',#126,#120,#68,.T.);
#154=EDGE_CURVE('',#127,#126,#69,.T.);
#155=EDGE_CURVE('',#128,#127,#70,.T.);
#156=EDGE_CURVE('',#124,#128,#71,.T.);
#157=EDGE_CURVE('',#129,#125,#72,.T.);
#158=EDGE_CURVE('',#129,#121,#73,.T.);
#159=EDGE_CURVE('',#130,#131,#74,.T.);
#160=EDGE_CURVE('',#132,#130,#75,.T.);
#161=EDGE_CURVE('',#133,#132,#76,.T.);
#162=EDGE_CURVE('',#134,#133,#77,.T.);
#163=EDGE_CURVE('',#134,#122,#78,.T.);
#164=EDGE_CURVE('',#131,#129,#79,.T.);
#165=EDGE_CURVE('',#126,#134,#80,.T.);
#166=EDGE_CURVE('',#123,#135,#81,.F.);
#167=EDGE_CURVE('',#136,#135,#21,.T.);
#168=EDGE_CURVE('',#136,#131,#82,.F.);
#169=EDGE_CURVE('',#124,#137,#83,.F.);
#170=EDGE_CURVE('',#135,#137,#17,.T.);
#171=EDGE_CURVE('',#138,#136,#18,.T.);
#172=EDGE_CURVE('',#138,#130,#84,.F.);
#173=EDGE_CURVE('',#139,#128,#85,.F.);
#174=EDGE_CURVE('',#137,#139,#22,.T.);
#175=EDGE_CURVE('',#140,#127,#86,.F.);
#176=EDGE_CURVE('',#139,#140,#19,.T.);
#177=EDGE_CURVE('',#141,#141,#23,.T.);
#178=EDGE_CURVE('',#141,#142,#24,.T.);
#179=EDGE_CURVE('',#142,#142,#25,.T.);
#180=EDGE_CURVE('',#142,#143,#87,.T.);
#181=EDGE_CURVE('',#143,#144,#26,.T.);
#182=EDGE_CURVE('',#144,#145,#20,.T.);
#183=EDGE_CURVE('',#145,#138,#27,.T.);
#184=EDGE_CURVE('',#140,#143,#28,.T.);
#185=EDGE_CURVE('',#132,#145,#88,.F.);
#186=EDGE_CURVE('',#133,#144,#89,.F.);
#187=ORIENTED_EDGE('',*,*,#146,.F.);
#188=ORIENTED_EDGE('',*,*,#147,.F.);
#189=ORIENTED_EDGE('',*,*,#148,.F.);
#190=ORIENTED_EDGE('',*,*,#149,.F.);
#191=ORIENTED_EDGE('',*,*,#150,.F.);
#192=ORIENTED_EDGE('',*,*,#151,.F.);
#193=ORIENTED_EDGE('',*,*,#152,.T.);
#194=ORIENTED_EDGE('',*,*,#146,.T.);
#195=ORIENTED_EDGE('',*,*,#153,.F.);
#196=ORIENTED_EDGE('',*,*,#154,.F.);
#197=ORIENTED_EDGE('',*,*,#155,.F.);
#198=ORIENTED_EDGE('',*,*,#156,.F.);
#199=ORIENTED_EDGE('',*,*,#157,.F.);
#200=ORIENTED_EDGE('',*,*,#158,.T.);
#201=ORIENTED_EDGE('',*,*,#147,.T.);
#202=ORIENTED_EDGE('',*,*,#152,.F.);
#203=ORIENTED_EDGE('',*,*,#159,.F.);
#204=ORIENTED_EDGE('',*,*,#160,.F.);
#205=ORIENTED_EDGE('',*,*,#161,.F.);
#206=ORIENTED_EDGE('',*,*,#162,.F.);
#207=ORIENTED_EDGE('',*,*,#163,.T.);
#208=ORIENTED_EDGE('',*,*,#148,.T.);
#209=ORIENTED_EDGE('',*,*,#158,.F.);
#210=ORIENTED_EDGE('',*,*,#164,.F.);
#211=ORIENTED_EDGE('',*,*,#165,.F.);
#212=ORIENTED_EDGE('',*,*,#153,.T.);
#213=ORIENTED_EDGE('',*,*,#149,.T.);
#214=ORIENTED_EDGE('',*,*,#163,.F.);
#215=ORIENTED_EDGE('',*,*,#164,.T.);
#216=ORIENTED_EDGE('',*,*,#157,.T.);
#217=ORIENTED_EDGE('',*,*,#151,.T.);
#218=ORIENTED_EDGE('',*,*,#166,.T.);
#219=ORIENTED_EDGE('',*,*,#167,.F.);
#220=ORIENTED_EDGE('',*,*,#168,.T.);
#221=ORIENTED_EDGE('',*,*,#150,.T.);
#222=ORIENTED_EDGE('',*,*,#169,.T.);
#223=ORIENTED_EDGE('',*,*,#170,.F.);
#224=ORIENTED_EDGE('',*,*,#166,.F.);
#225=ORIENTED_EDGE('',*,*,#159,.T.);
#226=ORIENTED_EDGE('',*,*,#168,.F.);
#227=ORIENTED_EDGE('',*,*,#171,.F.);
#228=ORIENTED_EDGE('',*,*,#172,.T.);
#229=ORIENTED_EDGE('',*,*,#156,.T.);
#230=ORIENTED_EDGE('',*,*,#173,.F.);
#231=ORIENTED_EDGE('',*,*,#174,.F.);
#232=ORIENTED_EDGE('',*,*,#169,.F.);
#233=ORIENTED_EDGE('',*,*,#155,.T.);
#234=ORIENTED_EDGE('',*,*,#175,.F.);
#235=ORIENTED_EDGE('',*,*,#176,.F.);
#236=ORIENTED_EDGE('',*,*,#173,.T.);
#237=ORIENTED_EDGE('',*,*,#177,.T.);
#238=ORIENTED_EDGE('',*,*,#178,.T.);
#239=ORIENTED_EDGE('',*,*,#179,.T.);
#240=ORIENTED_EDGE('',*,*,#178,.F.);
#241=ORIENTED_EDGE('',*,*,#179,.F.);
#242=ORIENTED_EDGE('',*,*,#180,.T.);
#243=ORIENTED_EDGE('',*,*,#181,.T.);
#244=ORIENTED_EDGE('',*,*,#182,.T.);
#245=ORIENTED_EDGE('',*,*,#183,.T.);
#246=ORIENTED_EDGE('',*,*,#171,.T.);
#247=ORIENTED_EDGE('',*,*,#167,.T.);
#248=ORIENTED_EDGE('',*,*,#170,.T.);
#249=ORIENTED_EDGE('',*,*,#174,.T.);
#250=ORIENTED_EDGE('',*,*,#176,.T.);
#251=ORIENTED_EDGE('',*,*,#184,.T.);
#252=ORIENTED_EDGE('',*,*,#180,.F.);
#253=ORIENTED_EDGE('',*,*,#177,.F.);
#254=ORIENTED_EDGE('',*,*,#161,.T.);
#255=ORIENTED_EDGE('',*,*,#185,.T.);
#256=ORIENTED_EDGE('',*,*,#182,.F.);
#257=ORIENTED_EDGE('',*,*,#186,.F.);
#258=ORIENTED_EDGE('',*,*,#160,.T.);
#259=ORIENTED_EDGE('',*,*,#172,.F.);
#260=ORIENTED_EDGE('',*,*,#183,.F.);
#261=ORIENTED_EDGE('',*,*,#185,.F.);
#262=ORIENTED_EDGE('',*,*,#162,.T.);
#263=ORIENTED_EDGE('',*,*,#186,.T.);
#264=ORIENTED_EDGE('',*,*,#181,.F.);
#265=ORIENTED_EDGE('',*,*,#184,.F.);
#266=ORIENTED_EDGE('',*,*,#175,.T.);
#267=ORIENTED_EDGE('',*,*,#154,.T.);
#268=ORIENTED_EDGE('',*,*,#165,.T.);
#269=PLANE('',#301);
#270=PLANE('',#302);
#271=PLANE('',#303);
#272=PLANE('',#304);
#273=PLANE('',#305);
#274=PLANE('',#306);
#275=PLANE('',#308);
#276=PLANE('',#310);
#277=PLANE('',#312);
#278=PLANE('',#314);
#279=PLANE('',#325);
#280=PLANE('',#326);
#281=PLANE('',#327);
#282=PLANE('',#328);
#283=ADVANCED_FACE('',(#29),#269,.F.);
#284=ADVANCED_FACE('',(#30),#270,.F.);
#285=ADVANCED_FACE('',(#31),#271,.F.);
#286=ADVANCED_FACE('',(#32),#272,.F.);
#287=ADVANCED_FACE('',(#33),#273,.F.);
#288=ADVANCED_FACE('',(#34),#274,.F.);
#289=ADVANCED_FACE('',(#35),#275,.F.);
#290=ADVANCED_FACE('',(#36),#276,.F.);
#291=ADVANCED_FACE('',(#37),#277,.F.);
#292=ADVANCED_FACE('',(#38),#278,.F.);
#293=ADVANCED_FACE('',(#39),#16,.T.);
#294=ADVANCED_FACE('',(#40),#15,.T.);
#295=ADVANCED_FACE('',(#41),#279,.T.);
#296=ADVANCED_FACE('',(#42),#280,.F.);
#297=ADVANCED_FACE('',(#43),#281,.F.);
#298=ADVANCED_FACE('',(#44),#282,.F.);
#299=CLOSED_SHELL('',(#283,#284,#285,#286,#287,#288,#289,#290,#291,#292,
#293,#294,#295,#296,#297,#298));
#300=AXIS2_PLACEMENT_3D('',#416,#329,#330);
#301=AXIS2_PLACEMENT_3D('',#417,#331,#332);
#302=AXIS2_PLACEMENT_3D('',#426,#337,#338);
#303=AXIS2_PLACEMENT_3D('',#440,#346,#347);
#304=AXIS2_PLACEMENT_3D('',#444,#350,#351);
#305=AXIS2_PLACEMENT_3D('',#456,#358,#359);
#306=AXIS2_PLACEMENT_3D('',#458,#361,#362);
#307=AXIS2_PLACEMENT_3D('',#462,#364,#365);
#308=AXIS2_PLACEMENT_3D('',#464,#367,#368);
#309=AXIS2_PLACEMENT_3D('',#467,#370,#371);
#310=AXIS2_PLACEMENT_3D('',#468,#372,#373);
#311=AXIS2_PLACEMENT_3D('',#470,#374,#375);
#312=AXIS2_PLACEMENT_3D('',#472,#377,#378);
#313=AXIS2_PLACEMENT_3D('',#475,#380,#381);
#314=AXIS2_PLACEMENT_3D('',#476,#382,#383);
#315=AXIS2_PLACEMENT_3D('',#479,#385,#386);
#316=AXIS2_PLACEMENT_3D('',#480,#387,#388);
#317=AXIS2_PLACEMENT_3D('',#482,#389,#390);
#318=AXIS2_PLACEMENT_3D('',#484,#391,#392);
#319=AXIS2_PLACEMENT_3D('',#485,#393,#394);
#320=AXIS2_PLACEMENT_3D('',#486,#395,#396);
#321=AXIS2_PLACEMENT_3D('',#490,#398,#399);
#322=AXIS2_PLACEMENT_3D('',#492,#400,#401);
#323=AXIS2_PLACEMENT_3D('',#493,#402,#403);
#324=AXIS2_PLACEMENT_3D('',#494,#404,#405);
#325=AXIS2_PLACEMENT_3D('',#495,#406,#407);
#326=AXIS2_PLACEMENT_3D('',#496,#408,#409);
#327=AXIS2_PLACEMENT_3D('',#499,#412,#413);
#328=AXIS2_PLACEMENT_3D('',#500,#414,#415);
#329=DIRECTION('axis',(0.,0.,1.));
#330=DIRECTION('refdir',(1.,0.,0.));
#331=DIRECTION('center_axis',(0.,0.,1.));
#332=DIRECTION('ref_axis',(1.,0.,0.));
#333=DIRECTION('',(-1.,0.,0.));
#334=DIRECTION('',(0.,1.,0.));
#335=DIRECTION('',(1.,0.,0.));
#336=DIRECTION('',(0.,-1.,0.));
#337=DIRECTION('center_axis',(0.,1.,0.));
#338=DIRECTION('ref_axis',(-1.,0.,0.));
#339=DIRECTION('',(-0.0523359562429438,0.,0.998629534754574));
#340=DIRECTION('',(-1.,0.,0.));
#341=DIRECTION('',(0.,0.,1.));
#342=DIRECTION('',(0.,0.,1.));
#343=DIRECTION('',(-1.,0.,0.));
#344=DIRECTION('',(-0.0523359562429438,0.,-0.998629534754574));
#345=DIRECTION('',(-1.,0.,0.));
#346=DIRECTION('center_axis',(1.,0.,0.));
#347=DIRECTION('ref_axis',(0.,1.,0.));
#348=DIRECTION('',(0.,1.,0.));
#349=DIRECTION('',(0.,0.,1.));
#350=DIRECTION('center_axis',(0.,-1.,0.));
#351=DIRECTION('ref_axis',(1.,0.,0.));
#352=DIRECTION('',(0.0523359562429438,0.,-0.998629534754574));
#353=DIRECTION('',(1.,0.,0.));
#354=DIRECTION('',(0.0523359562429438,0.,0.998629534754574));
#355=DIRECTION('',(1.,0.,0.));
#356=DIRECTION('',(0.,0.,1.));
#357=DIRECTION('',(1.,0.,0.));
#358=DIRECTION('center_axis',(-1.,0.,0.));
#359=DIRECTION('ref_axis',(0.,-1.,0.));
#360=DIRECTION('',(0.,-1.,0.));
#361=DIRECTION('center_axis',(0.,0.,1.));
#362=DIRECTION('ref_axis',(1.,0.,0.));
#363=DIRECTION('',(0.,-1.,0.));
#364=DIRECTION('center_axis',(0.,0.,1.));
#365=DIRECTION('ref_axis',(1.,0.,0.));
#366=DIRECTION('',(0.,-1.,0.));
#367=DIRECTION('center_axis',(0.998629534754574,0.,0.0523359562429438));
#368=DIRECTION('ref_axis',(0.,1.,0.));
#369=DIRECTION('',(0.,-1.,0.));
#370=DIRECTION('center_axis',(0.998629534754574,0.,0.0523359562429438));
#371=DIRECTION('ref_axis',(0.0523359562429438,0.,-0.998629534754574));
#372=DIRECTION('center_axis',(0.998629534754574,0.,0.0523359562429438));
#373=DIRECTION('ref_axis',(0.,1.,0.));
#374=DIRECTION('center_axis',(0.998629534754574,0.,0.0523359562429438));
#375=DIRECTION('ref_axis',(0.0523359562429438,0.,-0.998629534754574));
#376=DIRECTION('',(0.,-1.,0.));
#377=DIRECTION('center_axis',(0.,0.,1.));
#378=DIRECTION('ref_axis',(1.,0.,0.));
#379=DIRECTION('',(0.,1.,0.));
#380=DIRECTION('center_axis',(0.,0.,1.));
#381=DIRECTION('ref_axis',(1.,0.,0.));
#382=DIRECTION('center_axis',(-0.998629534754574,0.,0.0523359562429438));
#383=DIRECTION('ref_axis',(0.,-1.,0.));
#384=DIRECTION('',(0.,1.,0.));
#385=DIRECTION('center_axis',(-0.998629534754574,0.,0.0523359562429438));
#386=DIRECTION('ref_axis',(0.0523359562429438,0.,0.998629534754574));
#387=DIRECTION('center_axis',(0.,0.,1.));
#388=DIRECTION('ref_axis',(1.,0.,0.));
#389=DIRECTION('center_axis',(0.,0.,-1.));
#390=DIRECTION('ref_axis',(1.,0.,0.));
#391=DIRECTION('center_axis',(0.,-1.,0.));
#392=DIRECTION('ref_axis',(-1.,0.,0.));
#393=DIRECTION('center_axis',(0.,0.,1.));
#394=DIRECTION('ref_axis',(1.,0.,0.));
#395=DIRECTION('center_axis',(0.,0.,1.));
#396=DIRECTION('ref_axis',(1.,0.,0.));
#397=DIRECTION('',(0.,0.,-1.));
#398=DIRECTION('center_axis',(0.,0.,1.));
#399=DIRECTION('ref_axis',(1.,0.,0.));
#400=DIRECTION('center_axis',(-0.998629534754574,0.,0.0523359562429438));
#401=DIRECTION('ref_axis',(0.0523359562429438,0.,0.998629534754574));
#402=DIRECTION('center_axis',(0.,0.,1.));
#403=DIRECTION('ref_axis',(1.,0.,0.));
#404=DIRECTION('center_axis',(0.,0.,1.));
#405=DIRECTION('ref_axis',(1.,0.,0.));
#406=DIRECTION('center_axis',(0.,0.,1.));
#407=DIRECTION('ref_axis',(1.,0.,0.));
#408=DIRECTION('center_axis',(-0.998629534754574,0.,0.0523359562429438));
#409=DIRECTION('ref_axis',(0.,-1.,0.));
#410=DIRECTION('',(0.,1.,0.));
#411=DIRECTION('',(0.,1.,0.));
#412=DIRECTION('center_axis',(0.,0.,1.));
#413=DIRECTION('ref_axis',(1.,0.,0.));
#414=DIRECTION('center_axis',(0.,0.,1.));
#415=DIRECTION('ref_axis',(1.,0.,0.));
#416=CARTESIAN_POINT('',(0.,0.,0.));
#417=CARTESIAN_POINT('Origin',(-32.5,40.25,8.6));
#418=CARTESIAN_POINT('',(-31.25,41.5,8.6));
#419=CARTESIAN_POINT('',(-33.75,41.5,8.6));
#420=CARTESIAN_POINT('',(-33.75,41.5,8.6));
#421=CARTESIAN_POINT('',(-31.25,39.,8.6));
#422=CARTESIAN_POINT('',(-31.25,41.5,8.6));
#423=CARTESIAN_POINT('',(-33.75,39.,8.6));
#424=CARTESIAN_POINT('',(-31.25,39.,8.6));
#425=CARTESIAN_POINT('',(-33.75,39.,8.6));
#426=CARTESIAN_POINT('Origin',(-31.25,41.5,4.6));
#427=CARTESIAN_POINT('',(-31.3117572189413,41.5,4.6));
#428=CARTESIAN_POINT('',(-31.4165727775074,41.5,6.6));
#429=CARTESIAN_POINT('',(-31.3118701541713,41.5,4.60215493256167));
#430=CARTESIAN_POINT('',(-31.25,41.5,4.6));
#431=CARTESIAN_POINT('',(-31.875,41.5,4.6));
#432=CARTESIAN_POINT('',(-31.25,41.5,4.6));
#433=CARTESIAN_POINT('',(-33.75,41.5,4.6));
#434=CARTESIAN_POINT('',(-33.75,41.5,4.6));
#435=CARTESIAN_POINT('',(-33.6882427810587,41.5,4.6));
#436=CARTESIAN_POINT('',(-31.875,41.5,4.6));
#437=CARTESIAN_POINT('',(-33.5834272224926,41.5,6.6));
#438=CARTESIAN_POINT('',(-33.6847060304338,41.5,4.66748522210396));
#439=CARTESIAN_POINT('',(-31.875,41.5,6.6));
#440=CARTESIAN_POINT('Origin',(-31.25,39.,4.6));
#441=CARTESIAN_POINT('',(-31.25,39.,4.6));
#442=CARTESIAN_POINT('',(-31.25,39.625,4.6));
#443=CARTESIAN_POINT('',(-31.25,39.,4.6));
#444=CARTESIAN_POINT('Origin',(-33.75,39.,4.6));
#445=CARTESIAN_POINT('',(-31.4165727775074,39.,6.6));
#446=CARTESIAN_POINT('',(-31.3117572189413,39.,4.6));
#447=CARTESIAN_POINT('',(-31.3152939695662,39.,4.66748522210396));
#448=CARTESIAN_POINT('',(-33.5834272224926,39.,6.6));
#449=CARTESIAN_POINT('',(-33.125,39.,6.6));
#450=CARTESIAN_POINT('',(-33.6882427810587,39.,4.6));
#451=CARTESIAN_POINT('',(-33.6881298458287,39.,4.60215493256167));
#452=CARTESIAN_POINT('',(-33.75,39.,4.6));
#453=CARTESIAN_POINT('',(-33.125,39.,4.6));
#454=CARTESIAN_POINT('',(-33.75,39.,4.6));
#455=CARTESIAN_POINT('',(-33.125,39.,4.6));
#456=CARTESIAN_POINT('Origin',(-33.75,41.5,4.6));
#457=CARTESIAN_POINT('',(-33.75,40.875,4.6));
#458=CARTESIAN_POINT('Origin',(-32.5,40.25,4.6));
#459=CARTESIAN_POINT('',(-31.3117572189413,43.004646818244,4.6));
#460=CARTESIAN_POINT('',(-31.3117572189413,38.8190911978746,4.6));
#461=CARTESIAN_POINT('',(-31.3117572189413,37.495353181756,4.6));
#462=CARTESIAN_POINT('Origin',(-32.5,40.25,4.6));
#463=CARTESIAN_POINT('',(-31.3117572189413,38.8190911978746,4.6));
#464=CARTESIAN_POINT('Origin',(-31.312152245655,37.3881823957492,4.60753755871915));
#465=CARTESIAN_POINT('',(-31.4165727775074,43.0475320290502,6.6));
#466=CARTESIAN_POINT('',(-31.4165727775074,38.8190911978746,6.6));
#467=CARTESIAN_POINT('Origin',(-32.5,40.25,27.2730229235879));
#468=CARTESIAN_POINT('Origin',(-31.312152245655,37.3881823957492,4.60753755871915));
#469=CARTESIAN_POINT('',(-31.4165727775074,37.4524679709498,6.6));
#470=CARTESIAN_POINT('Origin',(-32.5,40.25,27.2730229235879));
#471=CARTESIAN_POINT('',(-31.4165727775074,38.8190911978746,6.6));
#472=CARTESIAN_POINT('Origin',(-32.5,40.25,6.6));
#473=CARTESIAN_POINT('',(-33.5834272224926,43.0475320290502,6.6));
#474=CARTESIAN_POINT('',(-33.5834272224926,41.6809088021254,6.6));
#475=CARTESIAN_POINT('Origin',(-32.5,40.25,6.6));
#476=CARTESIAN_POINT('Origin',(-33.687847754345,43.1118176042508,4.60753755871915));
#477=CARTESIAN_POINT('',(-33.6882427810587,43.004646818244,4.6));
#478=CARTESIAN_POINT('',(-33.6882427810587,41.6809088021254,4.6));
#479=CARTESIAN_POINT('Origin',(-32.5,40.25,27.2730229235879));
#480=CARTESIAN_POINT('Origin',(-32.5,40.25,9.9));
#481=CARTESIAN_POINT('',(-35.3,40.25,10.1));
#482=CARTESIAN_POINT('Origin',(-32.5,40.25,10.1));
#483=CARTESIAN_POINT('',(-35.5,40.25,9.9));
#484=CARTESIAN_POINT('Origin',(-35.3,40.25,9.9));
#485=CARTESIAN_POINT('Origin',(-32.5,40.25,9.9));
#486=CARTESIAN_POINT('Origin',(-32.5,40.25,4.6));
#487=CARTESIAN_POINT('',(-35.5,40.25,4.59999999999997));
#488=CARTESIAN_POINT('',(-35.5,40.25,4.6));
#489=CARTESIAN_POINT('',(-33.6882427810587,37.495353181756,4.6));
#490=CARTESIAN_POINT('Origin',(-32.5,40.25,4.6));
#491=CARTESIAN_POINT('',(-33.5834272224926,37.4524679709498,6.6));
#492=CARTESIAN_POINT('Origin',(-32.5,40.25,27.2730229235879));
#493=CARTESIAN_POINT('Origin',(-32.5,40.25,6.6));
#494=CARTESIAN_POINT('Origin',(-32.5,40.25,4.6));
#495=CARTESIAN_POINT('Origin',(-32.5,40.25,10.1));
#496=CARTESIAN_POINT('Origin',(-33.687847754345,43.1118176042508,4.60753755871915));
#497=CARTESIAN_POINT('',(-33.5834272224926,41.6809088021254,6.6));
#498=CARTESIAN_POINT('',(-33.6882427810587,41.6809088021254,4.6));
#499=CARTESIAN_POINT('Origin',(-32.5,40.25,6.6));
#500=CARTESIAN_POINT('Origin',(-32.5,40.25,4.6));
#501=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(0.01),#505,
'DISTANCE_ACCURACY_VALUE',
'Maximum model space distance between geometric entities at asserted c
onnectivities');
#502=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(0.01),#505,
'DISTANCE_ACCURACY_VALUE',
'Maximum model space distance between geometric entities at asserted c
onnectivities');
#503=(
GEOMETRIC_REPRESENTATION_CONTEXT(3)
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#501))
GLOBAL_UNIT_ASSIGNED_CONTEXT((#505,#506,#507))
REPRESENTATION_CONTEXT('','3D')
);
#504=(
GEOMETRIC_REPRESENTATION_CONTEXT(3)
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#502))
GLOBAL_UNIT_ASSIGNED_CONTEXT((#505,#506,#507))
REPRESENTATION_CONTEXT('','3D')
);
#505=(
LENGTH_UNIT()
NAMED_UNIT(*)
SI_UNIT(.MILLI.,.METRE.)
);
#506=(
NAMED_UNIT(*)
PLANE_ANGLE_UNIT()
SI_UNIT($,.RADIAN.)
);
#507=(
NAMED_UNIT(*)
SI_UNIT($,.STERADIAN.)
SOLID_ANGLE_UNIT()
);
#508=SHAPE_DEFINITION_REPRESENTATION(#509,#510);
#509=PRODUCT_DEFINITION_SHAPE('',$,#512);
#510=SHAPE_REPRESENTATION('',(#300),#503);
#511=PRODUCT_DEFINITION_CONTEXT('part definition',#516,'design');
#512=PRODUCT_DEFINITION('sc64_shell reworked','sc64_shell reworked v10',
#513,#511);
#513=PRODUCT_DEFINITION_FORMATION('',$,#518);
#514=PRODUCT_RELATED_PRODUCT_CATEGORY('sc64_shell reworked v10',
'sc64_shell reworked v10',(#518));
#515=APPLICATION_PROTOCOL_DEFINITION('international standard',
'automotive_design',2009,#516);
#516=APPLICATION_CONTEXT(
'Core Data for Automotive Mechanical Design Process');
#517=PRODUCT_CONTEXT('part definition',#516,'mechanical');
#518=PRODUCT('sc64_shell reworked','sc64_shell reworked v10',$,(#517));
#519=PRESENTATION_STYLE_ASSIGNMENT((#523));
#520=PRESENTATION_STYLE_ASSIGNMENT((#524));
#521=PRESENTATION_STYLE_ASSIGNMENT((#525));
#522=PRESENTATION_STYLE_ASSIGNMENT((#526));
#523=SURFACE_STYLE_USAGE(.BOTH.,#527);
#524=SURFACE_STYLE_USAGE(.BOTH.,#528);
#525=SURFACE_STYLE_USAGE(.BOTH.,#529);
#526=SURFACE_STYLE_USAGE(.BOTH.,#530);
#527=SURFACE_SIDE_STYLE('',(#531));
#528=SURFACE_SIDE_STYLE('',(#532));
#529=SURFACE_SIDE_STYLE('',(#533));
#530=SURFACE_SIDE_STYLE('',(#534));
#531=SURFACE_STYLE_FILL_AREA(#535);
#532=SURFACE_STYLE_FILL_AREA(#536);
#533=SURFACE_STYLE_FILL_AREA(#537);
#534=SURFACE_STYLE_FILL_AREA(#538);
#535=FILL_AREA_STYLE('Steel - Satin',(#539));
#536=FILL_AREA_STYLE('Plastic - Translucent Matte (Blue)',(#540));
#537=FILL_AREA_STYLE('Powder Coat - Rough (White)',(#541));
#538=FILL_AREA_STYLE('Plastic - Glossy (Black)',(#542));
#539=FILL_AREA_STYLE_COLOUR('Steel - Satin',#543);
#540=FILL_AREA_STYLE_COLOUR('Plastic - Translucent Matte (Blue)',#544);
#541=FILL_AREA_STYLE_COLOUR('Powder Coat - Rough (White)',#545);
#542=FILL_AREA_STYLE_COLOUR('Plastic - Glossy (Black)',#546);
#543=COLOUR_RGB('Steel - Satin',0.627450980392157,0.627450980392157,0.627450980392157);
#544=COLOUR_RGB('Plastic - Translucent Matte (Blue)',0.188235294117647,
0.231372549019608,0.588235294117647);
#545=COLOUR_RGB('Powder Coat - Rough (White)',0.964705882352941,0.964705882352941,
0.952941176470588);
#546=COLOUR_RGB('Plastic - Glossy (Black)',0.0980392156862745,0.0980392156862745,
0.0980392156862745);
ENDSEC;
END-ISO-10303-21;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ N64_ELFCOMPRESS = $(N64_BINDIR)/n64elfcompress
N64_TOOL = $(N64_BINDIR)/n64tool N64_TOOL = $(N64_BINDIR)/n64tool
PYTHON = python3 PYTHON = python3
FLAGS = -march=vr4300 -mtune=vr4300 $(USER_FLAGS) FLAGS = -march=vr4300 -mtune=vr4300 -mfix4300 $(USER_FLAGS)
CFLAGS = -Os -Wall -ffunction-sections -fdata-sections -ffreestanding -MMD -MP CFLAGS = -Os -Wall -ffunction-sections -fdata-sections -ffreestanding -MMD -MP
ASFLAGS = -Wa,-I$(N64_INST)/mips64-elf/lib ASFLAGS = -Wa,-I$(N64_INST)/mips64-elf/lib
LDFLAGS = -lc -nostartfiles -Wl,--gc-sections LDFLAGS = -lc -nostartfiles -Wl,--gc-sections
@ -31,8 +31,10 @@ SRC_FILES = \
exception.S \ exception.S \
font.c \ font.c \
init.c \ init.c \
interrupt.c \ interrupts.c \
interrupts.S \
io.c \ io.c \
joybus.c \
main.c \ main.c \
menu.c \ menu.c \
reboot.S \ reboot.S \

View File

@ -79,6 +79,7 @@ void boot (boot_params_t *params) {
while (cpu_io_read(&SP->DMA_BUSY)); while (cpu_io_read(&SP->DMA_BUSY));
cpu_io_write(&PI->SR, PI_SR_CLR_INTR | PI_SR_RESET); cpu_io_write(&PI->SR, PI_SR_CLR_INTR | PI_SR_RESET);
cpu_io_write(&SI->SR, 0);
while ((cpu_io_read(&VI->CURR_LINE) & ~(VI_CURR_LINE_FIELD)) != 0); while ((cpu_io_read(&VI->CURR_LINE) & ~(VI_CURR_LINE_FIELD)) != 0);
cpu_io_write(&VI->V_INTR, 0x3FF); cpu_io_write(&VI->V_INTR, 0x3FF);
cpu_io_write(&VI->H_LIMITS, 0); cpu_io_write(&VI->H_LIMITS, 0);
@ -146,5 +147,5 @@ void boot (boot_params_t *params) {
"t3" "t3"
); );
while (1); while (true);
} }

View File

@ -1,4 +1,3 @@
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include "display.h" #include "display.h"
#include "font.h" #include "font.h"
@ -117,8 +116,8 @@ static void display_draw_character (char c) {
} }
if ((char_x + FONT_WIDTH) > (SCREEN_WIDTH - BORDER_WIDTH)) { if ((char_x + FONT_WIDTH) > (SCREEN_WIDTH - BORDER_WIDTH)) {
char_x = BORDER_WIDTH; char_x -= FONT_WIDTH;
char_y += FONT_HEIGHT + LINE_SPACING; c = '\x7F';
} }
if ((c < ' ') || (c > '~')) { if ((c < ' ') || (c > '~')) {
@ -181,8 +180,12 @@ void display_init (uint32_t *background) {
} }
} }
bool display_ready (void) {
return vi_configured;
}
void display_vprintf (const char *fmt, va_list args) { void display_vprintf (const char *fmt, va_list args) {
char line[256]; char line[1024];
vsniprintf(line, sizeof(line), fmt, args); vsniprintf(line, sizeof(line), fmt, args);
display_draw_string(line); display_draw_string(line);

View File

@ -3,10 +3,12 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
void display_init (uint32_t *background); void display_init (uint32_t *background);
bool display_ready (void);
void display_vprintf (const char *fmt, va_list args); void display_vprintf (const char *fmt, va_list args);
void display_printf (const char* fmt, ...); void display_printf (const char* fmt, ...);

View File

@ -1,13 +1,23 @@
#include <stdarg.h> #include <stdarg.h>
#include "exception.h" #include "display.h"
#include "init.h"
#include "version.h"
#include "../assets/assets.h"
void error_display (const char *fmt, ...) { void error_display (const char *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); deinit();
EXCEPTION_TRIGGER(TRIGGER_CODE_ERROR);
va_end(args);
while (1); display_init((uint32_t *) (&assets_sc64_logo_640_240_dimmed));
version_print();
display_printf("[ Runtime error ]\n");
va_start(args, fmt);
display_vprintf(fmt, args);
va_end(args);
display_printf("\n");
while (true);
} }

View File

@ -1,8 +1,6 @@
#include "vr4300.h" #include "vr4300.h"
#define WATCHDOG_TIMEOUT (5 * (93750000UL / 2))
#define ZR_OFFSET (0) #define ZR_OFFSET (0)
#define AT_OFFSET (8) #define AT_OFFSET (8)
#define V0_OFFSET (16) #define V0_OFFSET (16)
@ -35,11 +33,13 @@
#define SP_OFFSET (232) #define SP_OFFSET (232)
#define S8_OFFSET (240) #define S8_OFFSET (240)
#define RA_OFFSET (248) #define RA_OFFSET (248)
#define C0_STATUS_OFFSET (256) #define HI_OFFSET (256)
#define C0_CAUSE_OFFSET (260) #define LO_OFFSET (264)
#define C0_EPC_OFFSET (264) #define C0_EPC_OFFSET (272)
#define C0_BADVADDR_OFFSET (272) #define C0_BADVADDR_OFFSET (280)
#define SAVE_REGISTERS_SIZE (280) #define C0_STATUS_OFFSET (288)
#define C0_CAUSE_OFFSET (292)
#define SAVE_REGISTERS_SIZE (296)
.section .text.exception_vector .section .text.exception_vector
@ -51,14 +51,18 @@ exception_xtlb_miss:
.org 0x0080 .org 0x0080
j exception_handler j exception_handler
exception_ecc:
.org 0x0100
j exception_handler
exception_other: exception_other:
.org 0x0180 .org 0x0180
j exception_handler j exception_handler
.section .text.exception_handler .section .text.exception_handler
.type exception_handler, %function
exception_handler: exception_handler:
.type exception_handler, %function
.set noat .set noat
la $k0, (_esp - SAVE_REGISTERS_SIZE) la $k0, (_esp - SAVE_REGISTERS_SIZE)
sd $zero, ZR_OFFSET($k0) sd $zero, ZR_OFFSET($k0)
@ -91,22 +95,20 @@ exception_handler:
sd $sp, SP_OFFSET($k0) sd $sp, SP_OFFSET($k0)
sd $s8, S8_OFFSET($k0) sd $s8, S8_OFFSET($k0)
sd $ra, RA_OFFSET($k0) sd $ra, RA_OFFSET($k0)
mfhi $t0
mflo $t1
sd $t0, HI_OFFSET($k0)
sd $t1, LO_OFFSET($k0)
.set at .set at
move $sp, $k0 move $sp, $k0
exception_check_type: exception_check_type:
mfc0 $a0, C0_CAUSE mfc0 $t0, C0_CAUSE
sw $a0, C0_CAUSE_OFFSET($k0) sw $t0, C0_CAUSE_OFFSET($k0)
move $a1, $a0 andi $a0, $t0, C0_CR_EC_MASK
move $t0, $a0 srl $a0, C0_CR_EC_BIT
andi $t0, C0_CR_IP7 beqz $a0, exception_interrupt
andi $a0, C0_CR_EC_MASK
srl $a0, $a0, C0_CR_EC_BIT
andi $a1, C0_CR_IP_MASK
srl $a1, $a1, C0_CR_IP_BIT
bne $t0, $zero, exception_fatal
beq $a0, $zero, exception_interrupt
exception_fatal: exception_fatal:
sd $k0, K0_OFFSET($k0) sd $k0, K0_OFFSET($k0)
@ -117,19 +119,27 @@ exception_fatal:
sd $t0, C0_EPC_OFFSET($k0) sd $t0, C0_EPC_OFFSET($k0)
dmfc0 $t0, C0_BADVADDR dmfc0 $t0, C0_BADVADDR
sd $t0, C0_BADVADDR_OFFSET($k0) sd $t0, C0_BADVADDR_OFFSET($k0)
move $a2, $k0 move $a1, $k0
la $t1, exception_fatal_handler jal exception_fatal_handler
jalr $t1
ld $t0, C0_EPC_OFFSET($k0) ld $t0, C0_EPC_OFFSET($k0)
dmtc0 $t0, C0_EPC dmtc0 $t0, C0_EPC
j exception_restore j exception_restore
exception_interrupt: exception_interrupt:
la $t1, exception_interrupt_handler andi $a0, $t0, C0_CR_IP_MASK
jalr $t1 srl $a0, C0_CR_IP_BIT
mfc0 $t0, C0_STATUS
andi $t0, C0_SR_IM_MASK
srl $t0, C0_SR_IM_BIT
and $a0, $t0
jal interrupts_handler
exception_restore: exception_restore:
.set noat .set noat
ld $t0, HI_OFFSET($k0)
ld $t1, LO_OFFSET($k0)
mthi $t0
mtlo $t1
ld $at, AT_OFFSET($k0) ld $at, AT_OFFSET($k0)
ld $v0, V0_OFFSET($k0) ld $v0, V0_OFFSET($k0)
ld $v1, V1_OFFSET($k0) ld $v1, V1_OFFSET($k0)
@ -162,51 +172,3 @@ exception_restore:
.set at .set at
eret eret
.section .text.exception_enable_interrupts
exception_enable_interrupts:
.type exception_enable_interrupts, %function
.global exception_enable_interrupts
mfc0 $t0, C0_STATUS
li $t1, C0_SR_IE
or $t0, $t0, $t1
mtc0 $t0, C0_STATUS
jr $ra
.section .text.exception_disable_interrupts
exception_disable_interrupts:
.type exception_disable_interrupts, %function
.global exception_disable_interrupts
mfc0 $t0, C0_STATUS
li $t1, ~(C0_SR_IE)
and $t0, $t0, $t1
mtc0 $t0, C0_STATUS
jr $ra
.section .text.exception_enable_watchdog
exception_enable_watchdog:
.type exception_enable_watchdog, %function
.global exception_enable_watchdog
mtc0 $zero, C0_COUNT
li $t1, WATCHDOG_TIMEOUT
mtc0 $t1, C0_COMPARE
mfc0 $t0, C0_STATUS
li $t1, C0_SR_IM7
or $t0, $t0, $t1
mtc0 $t0, C0_STATUS
jr $ra
.section .text.exception_disable_watchdog
exception_disable_watchdog:
.type exception_disable_watchdog, %function
.global exception_disable_watchdog
mfc0 $t0, C0_STATUS
li $t1, ~(C0_SR_IM7)
and $t0, $t0, $t1
mtc0 $t0, C0_STATUS
mtc0 $zero, C0_COMPARE
jr $ra

View File

@ -1,6 +1,5 @@
#include <stdarg.h> #include <stdarg.h>
#include "display.h" #include "display.h"
#include "exception_regs.h"
#include "exception.h" #include "exception.h"
#include "io.h" #include "io.h"
#include "version.h" #include "version.h"
@ -8,15 +7,6 @@
#include "../assets/assets.h" #include "../assets/assets.h"
#define EXCEPTION_INTERRUPT (0)
#define EXCEPTION_SYSCALL (8)
#define INTERRUPT_MASK_TIMER (1 << 7)
#define SYSCALL_CODE_MASK (0x03FFFFC0UL)
#define SYSCALL_CODE_BIT (6)
static const char *exception_get_description (uint8_t exception_code) { static const char *exception_get_description (uint8_t exception_code) {
switch (exception_code) { switch (exception_code) {
case 0: return "Interrupt"; case 0: return "Interrupt";
@ -41,48 +31,24 @@ static const char *exception_get_description (uint8_t exception_code) {
} }
void exception_fatal_handler (uint32_t exception_code, uint32_t interrupt_mask, exception_t *e) { void exception_fatal_handler (uint32_t exception_code, exception_t *e) {
version_t *version = version_get();
uint32_t *instruction_address = (((uint32_t *) (e->epc.u32)) + ((e->cr & C0_CR_BD) ? 1 : 0));
display_init((uint32_t *) (&assets_sc64_logo_640_240_dimmed)); display_init((uint32_t *) (&assets_sc64_logo_640_240_dimmed));
display_printf("[ SC64 bootloader metadata ]\n"); uint32_t exception_address = e->epc.u32 + (e->cr & C0_CR_BD ? 4 : 0);
display_printf("branch: %s | tag: %s\n", version->git_branch, version->git_tag);
display_printf("sha: %s\n", version->git_sha);
display_printf("msg: %s\n\n", version->git_message);
if (exception_code != EXCEPTION_SYSCALL) { version_print();
display_printf("%s\n", exception_get_description(exception_code)); display_printf("[ Unhandled exception ] @ 0x%08X\n", exception_address);
display_printf(" pc: 0x%08lX sr: 0x%08lX cr: 0x%08lX va: 0x%08lX\n", e->epc.u32, e->sr, e->cr, e->badvaddr.u32); display_printf("%s\n", exception_get_description(exception_code));
display_printf(" zr: 0x%08lX at: 0x%08lX v0: 0x%08lX v1: 0x%08lX\n", e->zr.u32, e->at.u32, e->v0.u32, e->v1.u32); display_printf(" pc: 0x%08lX sr: 0x%08lX cr: 0x%08lX va: 0x%08lX\n", e->epc.u32, e->sr, e->cr, e->badvaddr.u32);
display_printf(" a0: 0x%08lX a1: 0x%08lX a2: 0x%08lX a3: 0x%08lX\n", e->a0.u32, e->a1.u32, e->a2.u32, e->a3.u32); display_printf(" zr: 0x%08lX at: 0x%08lX v0: 0x%08lX v1: 0x%08lX\n", e->zr.u32, e->at.u32, e->v0.u32, e->v1.u32);
display_printf(" t0: 0x%08lX t1: 0x%08lX t2: 0x%08lX t3: 0x%08lX\n", e->t0.u32, e->t1.u32, e->t2.u32, e->t3.u32); display_printf(" a0: 0x%08lX a1: 0x%08lX a2: 0x%08lX a3: 0x%08lX\n", e->a0.u32, e->a1.u32, e->a2.u32, e->a3.u32);
display_printf(" t4: 0x%08lX t5: 0x%08lX t6: 0x%08lX t7: 0x%08lX\n", e->t4.u32, e->t5.u32, e->t6.u32, e->t7.u32); display_printf(" t0: 0x%08lX t1: 0x%08lX t2: 0x%08lX t3: 0x%08lX\n", e->t0.u32, e->t1.u32, e->t2.u32, e->t3.u32);
display_printf(" s0: 0x%08lX s1: 0x%08lX s2: 0x%08lX s3: 0x%08lX\n", e->s0.u32, e->s1.u32, e->s2.u32, e->s3.u32); display_printf(" t4: 0x%08lX t5: 0x%08lX t6: 0x%08lX t7: 0x%08lX\n", e->t4.u32, e->t5.u32, e->t6.u32, e->t7.u32);
display_printf(" s4: 0x%08lX s5: 0x%08lX s6: 0x%08lX s7: 0x%08lX\n", e->s4.u32, e->s5.u32, e->s6.u32, e->s7.u32); display_printf(" s0: 0x%08lX s1: 0x%08lX s2: 0x%08lX s3: 0x%08lX\n", e->s0.u32, e->s1.u32, e->s2.u32, e->s3.u32);
display_printf(" t8: 0x%08lX t9: 0x%08lX k0: 0x%08lX k1: 0x%08lX\n", e->t8.u32, e->t9.u32, e->k0.u32, e->k1.u32); display_printf(" s4: 0x%08lX s5: 0x%08lX s6: 0x%08lX s7: 0x%08lX\n", e->s4.u32, e->s5.u32, e->s6.u32, e->s7.u32);
display_printf(" gp: 0x%08lX sp: 0x%08lX s8: 0x%08lX ra: 0x%08lX\n\n", e->gp.u32, e->sp.u32, e->s8.u32, e->ra.u32); display_printf(" t8: 0x%08lX t9: 0x%08lX k0: 0x%08lX k1: 0x%08lX\n", e->t8.u32, e->t9.u32, e->k0.u32, e->k1.u32);
} else { display_printf(" gp: 0x%08lX sp: 0x%08lX s8: 0x%08lX ra: 0x%08lX\n", e->gp.u32, e->sp.u32, e->s8.u32, e->ra.u32);
display_printf("[ Runtime error ]\n"); display_printf(" hi: 0x%016lX lo: 0x%016lX\n", e->hi.u64, e->lo.u64);
}
if (exception_code == EXCEPTION_INTERRUPT) { while (true);
if (interrupt_mask & INTERRUPT_MASK_TIMER) {
exception_disable_watchdog();
display_printf("Still loading after 5 second limit...\n\n");
return;
}
} else if (exception_code == EXCEPTION_SYSCALL) {
uint32_t code = (((*instruction_address) & SYSCALL_CODE_MASK) >> SYSCALL_CODE_BIT);
if (code == TRIGGER_CODE_ERROR) {
const char *fmt = (const char *) (e->a0.u32);
va_list args = *((va_list *) (e->sp.u32));
display_vprintf(fmt, args);
display_printf("\n");
}
}
while (1);
} }

View File

@ -2,15 +2,57 @@
#define EXCEPTION_H__ #define EXCEPTION_H__
#define TRIGGER_CODE_ERROR (0) #include <stdint.h>
#define EXCEPTION_TRIGGER(code) { asm volatile ("syscall %[c]\n" :: [c] "i" (code)); }
void exception_enable_interrupts (void); typedef union {
void exception_disable_interrupts (void); uint64_t u64;
void exception_enable_watchdog (void); struct {
void exception_disable_watchdog (void); uint32_t u32_h;
uint32_t u32;
};
} uint64_32_t;
typedef struct {
uint64_32_t zr;
uint64_32_t at;
uint64_32_t v0;
uint64_32_t v1;
uint64_32_t a0;
uint64_32_t a1;
uint64_32_t a2;
uint64_32_t a3;
uint64_32_t t0;
uint64_32_t t1;
uint64_32_t t2;
uint64_32_t t3;
uint64_32_t t4;
uint64_32_t t5;
uint64_32_t t6;
uint64_32_t t7;
uint64_32_t s0;
uint64_32_t s1;
uint64_32_t s2;
uint64_32_t s3;
uint64_32_t s4;
uint64_32_t s5;
uint64_32_t s6;
uint64_32_t s7;
uint64_32_t t8;
uint64_32_t t9;
uint64_32_t k0;
uint64_32_t k1;
uint64_32_t gp;
uint64_32_t sp;
uint64_32_t s8;
uint64_32_t ra;
uint64_32_t hi;
uint64_32_t lo;
uint64_32_t epc;
uint64_32_t badvaddr;
uint32_t sr;
uint32_t cr;
} exception_t;
#endif #endif

View File

@ -1,56 +0,0 @@
#ifndef EXCEPTION_REGS_H__
#define EXCEPTION_REGS_H__
#include <stdint.h>
typedef union {
uint64_t u64;
struct {
uint32_t u32_h;
uint32_t u32;
};
} uint64_32_t;
typedef struct {
uint64_32_t zr;
uint64_32_t at;
uint64_32_t v0;
uint64_32_t v1;
uint64_32_t a0;
uint64_32_t a1;
uint64_32_t a2;
uint64_32_t a3;
uint64_32_t t0;
uint64_32_t t1;
uint64_32_t t2;
uint64_32_t t3;
uint64_32_t t4;
uint64_32_t t5;
uint64_32_t t6;
uint64_32_t t7;
uint64_32_t s0;
uint64_32_t s1;
uint64_32_t s2;
uint64_32_t s3;
uint64_32_t s4;
uint64_32_t s5;
uint64_32_t s6;
uint64_32_t s7;
uint64_32_t t8;
uint64_32_t t9;
uint64_32_t k0;
uint64_32_t k1;
uint64_32_t gp;
uint64_32_t sp;
uint64_32_t s8;
uint64_32_t ra;
uint32_t sr;
uint32_t cr;
uint64_32_t epc;
uint64_32_t badvaddr;
} exception_t;
#endif

View File

@ -367,3 +367,19 @@ R0.15 (November 6, 2022)
Fixed string functions cannot write the unicode characters not in BMP when FF_LFN_UNICODE == 2 (UTF-8). Fixed string functions cannot write the unicode characters not in BMP when FF_LFN_UNICODE == 2 (UTF-8).
Fixed a compatibility issue in identification of GPT header. Fixed a compatibility issue in identification of GPT header.
R0.15a (November 22, 2024)
Fixed a complie error when FF_FS_LOCK != 0.
Fixed a potential issue when work FatFs concurrency with FF_FS_REENTRANT, FF_VOLUMES >= 2 and FF_FS_LOCK > 0.
Made f_setlabel() accept a volume label in Unix style volume ID when FF_STR_VOLUME_ID == 2.
Made FatFs update PercInUse field in exFAT VBR. (A preceding f_getfree() is needed for the accuracy)
1. January 9, 2025
--------------------------------------------------------------------------------------------------
FatFs fails to load the FsInfo in FAT32 volumes and the f_getfree function will always be forced
a full FAT scan which takes a long time.
This problem was appeared at R0.15a and reported via an e-mail.
To fix this problem, apply ff15a_p1.diff to the ff.c.

View File

@ -10,6 +10,9 @@
#define BUFFER_BLOCKS_MAX (sizeof(SC64_BUFFERS->BUFFER) / SD_SECTOR_SIZE) #define BUFFER_BLOCKS_MAX (sizeof(SC64_BUFFERS->BUFFER) / SD_SECTOR_SIZE)
sc64_error_t sc64_error_fatfs;
DSTATUS disk_status (BYTE pdrv) { DSTATUS disk_status (BYTE pdrv) {
if (pdrv > 0) { if (pdrv > 0) {
return STA_NODISK; return STA_NODISK;
@ -20,7 +23,7 @@ DSTATUS disk_status (BYTE pdrv) {
sc64_sd_card_status_t sd_card_status; sc64_sd_card_status_t sd_card_status;
if ((error = sc64_sd_card_get_status(&sd_card_status)) != SC64_OK) { if ((error = sc64_sd_card_get_status(&sd_card_status)) != SC64_OK) {
error_display("Could not get SD card status: %d", error); error_display("Could not get SD card status\n (%08X) - %s", error, sc64_error_description(error));
} }
if (!(sd_card_status & SD_CARD_STATUS_INSERTED)) { if (!(sd_card_status & SD_CARD_STATUS_INSERTED)) {
@ -38,7 +41,7 @@ DSTATUS disk_initialize (BYTE pdrv) {
return STA_NODISK; return STA_NODISK;
} }
sc64_sd_card_init(); sc64_error_fatfs = sc64_sd_card_init();
return disk_status(pdrv); return disk_status(pdrv);
} }
@ -47,13 +50,16 @@ DRESULT disk_read (BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
if (pdrv > 0) { if (pdrv > 0) {
return RES_PARERR; return RES_PARERR;
} }
if ((sector + count) > 0x100000000ULL) {
return RES_PARERR;
}
uint32_t *physical_address = (uint32_t *) (PHYSICAL(buff)); uint32_t *physical_address = (uint32_t *) (PHYSICAL(buff));
if (physical_address < (uint32_t *) (N64_RAM_SIZE)) { if (physical_address < (uint32_t *) (N64_RAM_SIZE)) {
uint8_t aligned_buffer[BUFFER_BLOCKS_MAX * SD_SECTOR_SIZE] __attribute__((aligned(8))); uint8_t aligned_buffer[BUFFER_BLOCKS_MAX * SD_SECTOR_SIZE] __attribute__((aligned(8)));
while (count > 0) { while (count > 0) {
uint32_t blocks = ((count > BUFFER_BLOCKS_MAX) ? BUFFER_BLOCKS_MAX : count); uint32_t blocks = ((count > BUFFER_BLOCKS_MAX) ? BUFFER_BLOCKS_MAX : count);
size_t length = (blocks * SD_SECTOR_SIZE); size_t length = (blocks * SD_SECTOR_SIZE);
if (sc64_sd_read_sectors((uint32_t *) (SC64_BUFFERS->BUFFER), sector, blocks) != SC64_OK) { if ((sc64_error_fatfs = sc64_sd_read_sectors((uint32_t *) (SC64_BUFFERS->BUFFER), sector, blocks)) != SC64_OK) {
return RES_ERROR; return RES_ERROR;
} }
if (((uint32_t) (buff) % 8) == 0) { if (((uint32_t) (buff) % 8) == 0) {
@ -67,7 +73,7 @@ DRESULT disk_read (BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
count -= blocks; count -= blocks;
} }
} else { } else {
if (sc64_sd_read_sectors(physical_address, sector, count) != SC64_OK) { if ((sc64_error_fatfs = sc64_sd_read_sectors(physical_address, sector, count)) != SC64_OK) {
return RES_ERROR; return RES_ERROR;
} }
} }
@ -79,6 +85,9 @@ DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count) {
if (pdrv > 0) { if (pdrv > 0) {
return RES_PARERR; return RES_PARERR;
} }
if ((sector + count) > 0x100000000ULL) {
return RES_PARERR;
}
uint32_t *physical_address = (uint32_t *) (PHYSICAL(buff)); uint32_t *physical_address = (uint32_t *) (PHYSICAL(buff));
if (physical_address < (uint32_t *) (N64_RAM_SIZE)) { if (physical_address < (uint32_t *) (N64_RAM_SIZE)) {
uint8_t aligned_buffer[BUFFER_BLOCKS_MAX * SD_SECTOR_SIZE] __attribute__((aligned(8))); uint8_t aligned_buffer[BUFFER_BLOCKS_MAX * SD_SECTOR_SIZE] __attribute__((aligned(8)));
@ -91,7 +100,7 @@ DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count) {
memcpy(aligned_buffer, buff, length); memcpy(aligned_buffer, buff, length);
pi_dma_write((io32_t *) (SC64_BUFFERS->BUFFER), aligned_buffer, length); pi_dma_write((io32_t *) (SC64_BUFFERS->BUFFER), aligned_buffer, length);
} }
if (sc64_sd_write_sectors((uint32_t *) (SC64_BUFFERS->BUFFER), sector, blocks) != SC64_OK) { if ((sc64_error_fatfs = sc64_sd_write_sectors((uint32_t *) (SC64_BUFFERS->BUFFER), sector, blocks)) != SC64_OK) {
return RES_ERROR; return RES_ERROR;
} }
buff += length; buff += length;
@ -99,7 +108,7 @@ DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count) {
count -= blocks; count -= blocks;
} }
} else { } else {
if (sc64_sd_write_sectors(physical_address, sector, count) != SC64_OK) { if ((sc64_error_fatfs = sc64_sd_write_sectors(physical_address, sector, count)) != SC64_OK) {
return RES_ERROR; return RES_ERROR;
} }
} }
@ -122,7 +131,7 @@ DWORD get_fattime(void) {
sc64_rtc_time_t t; sc64_rtc_time_t t;
if ((error = sc64_get_time(&t)) != SC64_OK) { if ((error = sc64_get_time(&t)) != SC64_OK) {
error_display("Command TIME_GET failed: %d", error); error_display("Command TIME_GET failed\n (%08X) - %s", error, sc64_error_description(error));
} }
return ( return (

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
/*----------------------------------------------------------------------------/ /*----------------------------------------------------------------------------/
/ FatFs - Generic FAT Filesystem module R0.15 / / FatFs - Generic FAT Filesystem module R0.15a /
/-----------------------------------------------------------------------------/ /-----------------------------------------------------------------------------/
/ /
/ Copyright (C) 2022, ChaN, all right reserved. / Copyright (C) 2024, ChaN, all right reserved.
/ /
/ FatFs module is an open source software. Redistribution and use of FatFs in / FatFs module is an open source software. Redistribution and use of FatFs in
/ source and binary forms, with or without modification, are permitted provided / source and binary forms, with or without modification, are permitted provided
@ -20,14 +20,15 @@
#ifndef FF_DEFINED #ifndef FF_DEFINED
#define FF_DEFINED 80286 /* Revision ID */ #define FF_DEFINED 5380 /* Revision ID */
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#if !defined(FFCONF_DEF)
#include "ffconf.h" /* FatFs configuration options */ #include "ffconf.h" /* FatFs configuration options */
#endif
#if FF_DEFINED != FFCONF_DEF #if FF_DEFINED != FFCONF_DEF
#error Wrong configuration file (ffconf.h). #error Wrong configuration file (ffconf.h).
#endif #endif
@ -48,18 +49,18 @@ typedef unsigned __int64 QWORD;
#include <stdint.h> #include <stdint.h>
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
typedef unsigned char BYTE; /* char must be 8-bit */ typedef unsigned char BYTE; /* char must be 8-bit */
typedef uint16_t WORD; /* 16-bit unsigned integer */ typedef uint16_t WORD; /* 16-bit unsigned */
typedef uint32_t DWORD; /* 32-bit unsigned integer */ typedef uint32_t DWORD; /* 32-bit unsigned */
typedef uint64_t QWORD; /* 64-bit unsigned integer */ typedef uint64_t QWORD; /* 64-bit unsigned */
typedef WORD WCHAR; /* UTF-16 character type */ typedef WORD WCHAR; /* UTF-16 code unit */
#else /* Earlier than C99 */ #else /* Earlier than C99 */
#define FF_INTDEF 1 #define FF_INTDEF 1
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
typedef unsigned char BYTE; /* char must be 8-bit */ typedef unsigned char BYTE; /* char must be 8-bit */
typedef unsigned short WORD; /* 16-bit unsigned integer */ typedef unsigned short WORD; /* short must be 16-bit */
typedef unsigned long DWORD; /* 32-bit unsigned integer */ typedef unsigned long DWORD; /* long must be 32-bit */
typedef WORD WCHAR; /* UTF-16 character type */ typedef WORD WCHAR; /* UTF-16 code unit */
#endif #endif
@ -113,15 +114,15 @@ typedef char TCHAR;
#if FF_MULTI_PARTITION /* Multiple partition configuration */ #if FF_MULTI_PARTITION /* Multiple partition configuration */
typedef struct { typedef struct {
BYTE pd; /* Physical drive number */ BYTE pd; /* Associated physical drive */
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ BYTE pt; /* Associated partition (0:Auto detect, 1-4:Forced partition) */
} PARTITION; } PARTITION;
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */ extern PARTITION VolToPart[]; /* Volume to partition mapping table */
#endif #endif
#if FF_STR_VOLUME_ID #if FF_STR_VOLUME_ID
#ifndef FF_VOLUME_STRS #ifndef FF_VOLUME_STRS
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */ extern const char* VolumeStr[FF_VOLUMES]; /* User defined volume ID table */
#endif #endif
#endif #endif
@ -130,12 +131,12 @@ extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
/* Filesystem object structure (FATFS) */ /* Filesystem object structure (FATFS) */
typedef struct { typedef struct {
BYTE fs_type; /* Filesystem type (0:not mounted) */ BYTE fs_type; /* Filesystem type (0:blank filesystem object) */
BYTE pdrv; /* Volume hosting physical drive */ BYTE pdrv; /* Volume hosting physical drive */
BYTE ldrv; /* Logical drive number (used only when FF_FS_REENTRANT) */ BYTE ldrv; /* Logical drive number (used only when FF_FS_REENTRANT) */
BYTE n_fats; /* Number of FATs (1 or 2) */ BYTE n_fats; /* Number of FATs (1 or 2) */
BYTE wflag; /* win[] status (b0:dirty) */ BYTE wflag; /* win[] status (1:dirty) */
BYTE fsi_flag; /* FSINFO status (b7:disabled, b0:dirty) */ BYTE fsi_flag; /* Allocation information control (b7:disabled, b0:dirty) */
WORD id; /* Volume mount ID */ WORD id; /* Volume mount ID */
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
WORD csize; /* Cluster size [sectors] */ WORD csize; /* Cluster size [sectors] */
@ -146,11 +147,11 @@ typedef struct {
WCHAR* lfnbuf; /* LFN working buffer */ WCHAR* lfnbuf; /* LFN working buffer */
#endif #endif
#if FF_FS_EXFAT #if FF_FS_EXFAT
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */ BYTE* dirbuf; /* Directory entry block scratch pad buffer for exFAT */
#endif #endif
#if !FF_FS_READONLY #if !FF_FS_READONLY
DWORD last_clst; /* Last allocated cluster */ DWORD last_clst; /* Last allocated cluster (Unknown if >= n_fatent) */
DWORD free_clst; /* Number of free clusters */ DWORD free_clst; /* Number of free clusters (Unknown if >= n_fatent-2) */
#endif #endif
#if FF_FS_RPATH #if FF_FS_RPATH
DWORD cdir; /* Current directory start cluster (0:root) */ DWORD cdir; /* Current directory start cluster (0:root) */
@ -272,24 +273,24 @@ typedef struct {
/* File function return code (FRESULT) */ /* File function return code (FRESULT) */
typedef enum { typedef enum {
FR_OK = 0, /* (0) Succeeded */ FR_OK = 0, /* (0) Function succeeded */
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
FR_INT_ERR, /* (2) Assertion failed */ FR_INT_ERR, /* (2) Assertion failed */
FR_NOT_READY, /* (3) The physical drive cannot work */ FR_NOT_READY, /* (3) The physical drive does not work */
FR_NO_FILE, /* (4) Could not find the file */ FR_NO_FILE, /* (4) Could not find the file */
FR_NO_PATH, /* (5) Could not find the path */ FR_NO_PATH, /* (5) Could not find the path */
FR_INVALID_NAME, /* (6) The path name format is invalid */ FR_INVALID_NAME, /* (6) The path name format is invalid */
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ FR_DENIED, /* (7) Access denied due to a prohibited access or directory full */
FR_EXIST, /* (8) Access denied due to prohibited access */ FR_EXIST, /* (8) Access denied due to a prohibited access */
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
FR_NOT_ENABLED, /* (12) The volume has no work area */ FR_NOT_ENABLED, /* (12) The volume has no work area */
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ FR_NO_FILESYSTEM, /* (13) Could not find a valid FAT volume */
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */ FR_MKFS_ABORTED, /* (14) The f_mkfs function aborted due to some problem */
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ FR_TIMEOUT, /* (15) Could not take control of the volume within defined period */
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated or given buffer is insufficient in size */
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */ FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
} FRESULT; } FRESULT;
@ -375,7 +376,7 @@ DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
void* ff_memalloc (UINT msize); /* Allocate memory block */ void* ff_memalloc (UINT msize); /* Allocate memory block */
void ff_memfree (void* mblock); /* Free memory block */ void ff_memfree (void* mblock); /* Free memory block */
#endif #endif
#if FF_FS_REENTRANT /* Sync functions */ #if FF_FS_REENTRANT /* Sync functions */
int ff_mutex_create (int vol); /* Create a sync object */ int ff_mutex_create (int vol); /* Create a sync object */
void ff_mutex_delete (int vol); /* Delete a sync object */ void ff_mutex_delete (int vol); /* Delete a sync object */
int ff_mutex_take (int vol); /* Lock sync object */ int ff_mutex_take (int vol); /* Lock sync object */
@ -389,7 +390,7 @@ void ff_mutex_give (int vol); /* Unlock sync object */
/* Flags and Offset Address */ /* Flags and Offset Address */
/*--------------------------------------------------------------*/ /*--------------------------------------------------------------*/
/* File access mode and open method flags (3rd argument of f_open) */ /* File access mode and open method flags (3rd argument of f_open function) */
#define FA_READ 0x01 #define FA_READ 0x01
#define FA_WRITE 0x02 #define FA_WRITE 0x02
#define FA_OPEN_EXISTING 0x00 #define FA_OPEN_EXISTING 0x00
@ -398,10 +399,10 @@ void ff_mutex_give (int vol); /* Unlock sync object */
#define FA_OPEN_ALWAYS 0x10 #define FA_OPEN_ALWAYS 0x10
#define FA_OPEN_APPEND 0x30 #define FA_OPEN_APPEND 0x30
/* Fast seek controls (2nd argument of f_lseek) */ /* Fast seek controls (2nd argument of f_lseek function) */
#define CREATE_LINKMAP ((FSIZE_t)0 - 1) #define CREATE_LINKMAP ((FSIZE_t)0 - 1)
/* Format options (2nd argument of f_mkfs) */ /* Format options (2nd argument of f_mkfs function) */
#define FM_FAT 0x01 #define FM_FAT 0x01
#define FM_FAT32 0x02 #define FM_FAT32 0x02
#define FM_EXFAT 0x04 #define FM_EXFAT 0x04

View File

@ -2,7 +2,7 @@
/ Configurations of FatFs Module / Configurations of FatFs Module
/---------------------------------------------------------------------------*/ /---------------------------------------------------------------------------*/
#define FFCONF_DEF 80286 /* Revision ID */ #define FFCONF_DEF 5380 /* Revision ID */
/*---------------------------------------------------------------------------/ /*---------------------------------------------------------------------------/
/ Function Configurations / Function Configurations
@ -15,7 +15,7 @@
/ and optional writing functions as well. */ / and optional writing functions as well. */
#define FF_FS_MINIMIZE 2 #define FF_FS_MINIMIZE 0
/* This option defines minimization level to remove some basic API functions. /* This option defines minimization level to remove some basic API functions.
/ /
/ 0: Basic functions are fully enabled. / 0: Basic functions are fully enabled.
@ -30,46 +30,46 @@
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
#define FF_USE_MKFS 0 #define FF_USE_MKFS 1
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ /* This option switches f_mkfs(). (0:Disable or 1:Enable) */
#define FF_USE_FASTSEEK 0 #define FF_USE_FASTSEEK 0
/* This option switches fast seek function. (0:Disable or 1:Enable) */ /* This option switches fast seek feature. (0:Disable or 1:Enable) */
#define FF_USE_EXPAND 0 #define FF_USE_EXPAND 0
/* This option switches f_expand function. (0:Disable or 1:Enable) */ /* This option switches f_expand(). (0:Disable or 1:Enable) */
#define FF_USE_CHMOD 0 #define FF_USE_CHMOD 0
/* This option switches attribute manipulation functions, f_chmod() and f_utime(). /* This option switches attribute control API functions, f_chmod() and f_utime().
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */ / (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
#define FF_USE_LABEL 0 #define FF_USE_LABEL 1
/* This option switches volume label functions, f_getlabel() and f_setlabel(). /* This option switches volume label API functions, f_getlabel() and f_setlabel().
/ (0:Disable or 1:Enable) */ / (0:Disable or 1:Enable) */
#define FF_USE_FORWARD 0 #define FF_USE_FORWARD 0
/* This option switches f_forward() function. (0:Disable or 1:Enable) */ /* This option switches f_forward(). (0:Disable or 1:Enable) */
#define FF_USE_STRFUNC 0 #define FF_USE_STRFUNC 0
#define FF_PRINT_LLI 1 #define FF_PRINT_LLI 0
#define FF_PRINT_FLOAT 1 #define FF_PRINT_FLOAT 0
#define FF_STRF_ENCODE 3 #define FF_STRF_ENCODE 3
/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and /* FF_USE_STRFUNC switches the string API functions, f_gets(), f_putc(), f_puts()
/ f_printf(). / and f_printf().
/ /
/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect. / 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
/ 1: Enable without LF-CRLF conversion. / 1: Enable without LF - CRLF conversion.
/ 2: Enable with LF-CRLF conversion. / 2: Enable with LF - CRLF conversion.
/ /
/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2 / FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
/ makes f_printf() support floating point argument. These features want C99 or later. / makes f_printf() support floating point argument. These features want C99 or later.
/ When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character / When FF_LFN_UNICODE >= 1 with LFN enabled, string API functions convert the character
/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE / encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
/ to be read/written via those functions. / to be read/written via those functions.
/ /
@ -118,22 +118,22 @@
/* The FF_USE_LFN switches the support for LFN (long file name). /* The FF_USE_LFN switches the support for LFN (long file name).
/ /
/ 0: Disable LFN. FF_MAX_LFN has no effect. / 0: Disable LFN. FF_MAX_LFN has no effect.
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/ 2: Enable LFN with dynamic working buffer on the STACK. / 2: Enable LFN with dynamic working buffer on the STACK.
/ 3: Enable LFN with dynamic working buffer on the HEAP. / 3: Enable LFN with dynamic working buffer on the HEAP.
/ /
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function / To enable the LFN, ffunicode.c needs to be added to the project. The LFN feature
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and / requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. / additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can / The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
/ be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN / be in range of 12 to 255. It is recommended to be set 255 to fully support the LFN
/ specification. / specification.
/ When use stack for the working buffer, take care on stack overflow. When use heap / When use stack for the working buffer, take care on stack overflow. When use heap
/ memory for the working buffer, memory management functions, ff_memalloc() and / memory for the working buffer, memory management functions, ff_memalloc() and
/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */ / ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
#define FF_LFN_UNICODE 0 #define FF_LFN_UNICODE 2
/* This option switches the character encoding on the API when LFN is enabled. /* This option switches the character encoding on the API when LFN is enabled.
/ /
/ 0: ANSI/OEM in current CP (TCHAR = char) / 0: ANSI/OEM in current CP (TCHAR = char)
@ -156,9 +156,9 @@
#define FF_FS_RPATH 0 #define FF_FS_RPATH 0
/* This option configures support for relative path. /* This option configures support for relative path.
/ /
/ 0: Disable relative path and remove related functions. / 0: Disable relative path and remove related API functions.
/ 1: Enable relative path. f_chdir() and f_chdrive() are available. / 1: Enable relative path. f_chdir() and f_chdrive() are available.
/ 2: f_getcwd() function is available in addition to 1. / 2: f_getcwd() is available in addition to 1.
*/ */
@ -175,7 +175,7 @@
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings. /* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive / When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each / number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid / logical drive. Number of items must not be less than FF_VOLUMES. Valid
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are / characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is / compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
/ not defined, a user defined volume string table is needed as: / not defined, a user defined volume string table is needed as:
@ -188,9 +188,9 @@
/* This option switches support for multiple volumes on the physical drive. /* This option switches support for multiple volumes on the physical drive.
/ By default (0), each logical drive number is bound to the same physical drive / By default (0), each logical drive number is bound to the same physical drive
/ number and only an FAT volume found on the physical drive will be mounted. / number and only an FAT volume found on the physical drive will be mounted.
/ When this function is enabled (1), each logical drive number can be bound to / When this feature is enabled (1), each logical drive number can be bound to
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() / arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
/ function will be available. */ / will be available. */
#define FF_MIN_SS 512 #define FF_MIN_SS 512
@ -198,25 +198,25 @@
/* This set of options configures the range of sector size to be supported. (512, /* This set of options configures the range of sector size to be supported. (512,
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and / 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
/ harddisk, but a larger value may be required for on-board flash memory and some / harddisk, but a larger value may be required for on-board flash memory and some
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured / type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is
/ for variable sector size mode and disk_ioctl() function needs to implement / configured for variable sector size mode and disk_ioctl() needs to implement
/ GET_SECTOR_SIZE command. */ / GET_SECTOR_SIZE command. */
#define FF_LBA64 0 #define FF_LBA64 1
/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable) /* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */ / To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
#define FF_MIN_GPT 0x10000000 #define FF_MIN_GPT 0x10000000
/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and /* Minimum number of sectors to switch GPT as partitioning format in f_mkfs() and
/ f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */ / f_fdisk(). 2^32 sectors maximum. This option has no effect when FF_LBA64 == 0. */
#define FF_USE_TRIM 0 #define FF_USE_TRIM 0
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) /* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
/ To enable Trim function, also CTRL_TRIM command should be implemented to the / To enable this feature, also CTRL_TRIM command should be implemented to
/ disk_ioctl() function. */ / the disk_ioctl(). */
@ -240,20 +240,20 @@
#define FF_FS_NORTC 0 #define FF_FS_NORTC 0
#define FF_NORTC_MON 1 #define FF_NORTC_MON 1
#define FF_NORTC_MDAY 1 #define FF_NORTC_MDAY 1
#define FF_NORTC_YEAR 2022 #define FF_NORTC_YEAR 2025
/* The option FF_FS_NORTC switches timestamp feature. If the system does not have /* The option FF_FS_NORTC switches timestamp feature. If the system does not have
/ an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the / an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the
/ timestamp feature. Every object modified by FatFs will have a fixed timestamp / timestamp feature. Every object modified by FatFs will have a fixed timestamp
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time. / defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be / To enable timestamp function (FF_FS_NORTC = 0), get_fattime() need to be added
/ added to the project to read current time form real-time clock. FF_NORTC_MON, / to the project to read current time form real-time clock. FF_NORTC_MON,
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. / FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */ / These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
#define FF_FS_NOFSINFO 0 #define FF_FS_NOFSINFO 0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this /* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() function at the first time after volume mount will force / option, and f_getfree() at the first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number. / a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/ /
/ bit0=0: Use free cluster count in the FSINFO if available. / bit0=0: Use free cluster count in the FSINFO if available.
@ -280,13 +280,13 @@
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs /* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
/ module itself. Note that regardless of this option, file access to different / module itself. Note that regardless of this option, file access to different
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs() / volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/ and f_fdisk() function, are always not re-entrant. Only file/directory access / and f_fdisk(), are always not re-entrant. Only file/directory access to
/ to the same volume is under control of this featuer. / the same volume is under control of this featuer.
/ /
/ 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect. / 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect.
/ 1: Enable re-entrancy. Also user provided synchronization handlers, / 1: Enable re-entrancy. Also user provided synchronization handlers,
/ ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give() / ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give(),
/ function, must be added to the project. Samples are available in ffsystem.c. / must be added to the project. Samples are available in ffsystem.c.
/ /
/ The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick. / The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick.
*/ */

View File

@ -68,7 +68,7 @@ const uint8_t font_data[96][FONT_CHAR_BYTES] = {
{ 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, 0x00, }, { 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x38, 0x00, },
{ 0x00, 0x38, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, }, { 0x00, 0x38, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, },
{ 0x00, 0x18, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, }, { 0x1C, 0x63, 0x63, 0x1C, 0x00, 0x00, 0x00, 0x00, },
{ 0x00, 0x3C, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, }, { 0x00, 0x3C, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, },
{ 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00, }, { 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00, },
{ 0x00, 0x3C, 0x66, 0x06, 0x06, 0x06, 0x7C, 0x00, }, { 0x00, 0x3C, 0x66, 0x06, 0x06, 0x06, 0x7C, 0x00, },

View File

@ -1,6 +1,6 @@
#include "error.h" #include "error.h"
#include "exception.h"
#include "init.h" #include "init.h"
#include "interrupts.h"
#include "io.h" #include "io.h"
#include "sc64.h" #include "sc64.h"
#include "test.h" #include "test.h"
@ -8,13 +8,15 @@
init_tv_type_t __tv_type; init_tv_type_t __tv_type;
init_reset_type_t __reset_type; init_reset_type_t __reset_type;
uint32_t __entropy;
void init (init_tv_type_t tv_type, init_reset_type_t reset_type) { void init (init_tv_type_t tv_type, init_reset_type_t reset_type, uint32_t entropy) {
sc64_error_t error; sc64_error_t error;
__tv_type = tv_type; __tv_type = tv_type;
__reset_type = reset_type; __reset_type = reset_type;
__entropy = entropy;
sc64_unlock(); sc64_unlock();
@ -22,22 +24,22 @@ void init (init_tv_type_t tv_type, init_reset_type_t reset_type) {
error_display("SC64 hardware not detected"); error_display("SC64 hardware not detected");
} }
exception_enable_watchdog(); interrupts_init();
exception_enable_interrupts(); interrupts_start_watchdog();
if ((error = sc64_set_config(CFG_ID_BOOTLOADER_SWITCH, false)) != SC64_OK) { if ((error = sc64_set_config(CFG_ID_BOOTLOADER_SWITCH, false)) != SC64_OK) {
error_display("Command CONFIG_SET [BOOTLOADER_SWITCH] failed: %d", error); error_display("Command CONFIG_SET [BOOTLOADER_SWITCH] failed\n (%08X) - %s", error, sc64_error_description(error));
} }
if (test_check()) { if (test_check()) {
exception_disable_watchdog(); interrupts_stop_watchdog();
test_execute(); test_execute();
} }
} }
void deinit (void) { void deinit (void) {
exception_disable_interrupts(); interrupts_stop_watchdog();
exception_disable_watchdog(); interrupts_disable();
sc64_lock(); sc64_lock();
} }

View File

@ -2,6 +2,9 @@
#define INIT_H__ #define INIT_H__
#include <stdint.h>
typedef enum { typedef enum {
INIT_TV_TYPE_PAL = 0, INIT_TV_TYPE_PAL = 0,
INIT_TV_TYPE_NTSC = 1, INIT_TV_TYPE_NTSC = 1,
@ -16,9 +19,10 @@ typedef enum {
extern init_tv_type_t __tv_type; extern init_tv_type_t __tv_type;
extern init_reset_type_t __reset_type; extern init_reset_type_t __reset_type;
extern uint32_t __entropy;
void init (init_tv_type_t tv_type, init_reset_type_t reset_type); void init (init_tv_type_t tv_type, init_reset_type_t reset_type, uint32_t entropy);
void deinit (void); void deinit (void);

View File

@ -1,6 +0,0 @@
#include "exception_regs.h"
void exception_interrupt_handler (uint32_t exception_code, uint32_t interrupt_mask, exception_t *e) {
while (1);
}

View File

@ -0,0 +1,58 @@
#include "vr4300.h"
#define WATCHDOG_TIMEOUT (5 * (93750000UL / 2))
.section .text.interrupts
.type interrupts_init, %function
.global interrupts_init
interrupts_init:
li $t1, (C0_SR_IM4 | C0_SR_IM3 | C0_SR_IE)
mfc0 $t0, C0_STATUS
or $t0, $t1
mtc0 $t0, C0_STATUS
jr $ra
.type interrupts_disable, %function
.global interrupts_disable
interrupts_disable:
li $t0, ~(C0_SR_IE)
mfc0 $v0, C0_STATUS
and $t0, $v0
mtc0 $t0, C0_STATUS
jr $ra
.type interrupts_restore, %function
.global interrupts_restore
interrupts_restore:
mtc0 $a0, C0_STATUS
jr $ra
.type interrupts_start_watchdog, %function
.global interrupts_start_watchdog
interrupts_start_watchdog:
mtc0 $zero, C0_COUNT
li $t1, WATCHDOG_TIMEOUT
mtc0 $t1, C0_COMPARE
li $t1, C0_SR_IM7
mfc0 $t0, C0_STATUS
or $t0, $t1
mtc0 $t0, C0_STATUS
jr $ra
.type interrupts_stop_watchdog, %function
.global interrupts_stop_watchdog
interrupts_stop_watchdog:
li $t1, ~(C0_SR_IM7)
mfc0 $t0, C0_STATUS
and $t0, $t1
mtc0 $t0, C0_STATUS
mtc0 $zero, C0_COMPARE
jr $ra

View File

@ -0,0 +1,87 @@
#include "display.h"
#include "sc64.h"
#include "version.h"
#include "../assets/assets.h"
typedef enum {
INTERRUPT_NONE = 0,
INTERRUPT_SW_0 = (1 << 0),
INTERRUPT_SW_1 = (1 << 1),
INTERRUPT_RCP = (1 << 2),
INTERRUPT_CART = (1 << 3),
INTERRUPT_PRENMI = (1 << 4),
INTERRUPT_HW_5 = (1 << 5),
INTERRUPT_HW_6 = (1 << 6),
INTERRUPT_TIMER = (1 << 7),
} interrupt_t;
void interrupts_handler (uint8_t interrupts) {
if (interrupts == INTERRUPT_NONE) {
display_init((uint32_t *) (&assets_sc64_logo_640_240_dimmed));
version_print();
display_printf("[ Empty interrupt ]\n");
display_printf("There is no interrupt to handle\n");
while (true);
}
if (interrupts & INTERRUPT_CART) {
interrupts &= ~(INTERRUPT_CART);
sc64_irq_t irq = sc64_irq_pending();
if (irq != SC64_IRQ_NONE) {
sc64_irq_callback(irq);
}
}
if (interrupts & INTERRUPT_PRENMI) {
interrupts &= ~(INTERRUPT_PRENMI);
if (display_ready()) {
display_init(NULL);
display_printf("Resetting...\n");
}
while (true);
}
if (interrupts & INTERRUPT_TIMER) {
interrupts &= ~(INTERRUPT_TIMER);
display_init((uint32_t *) (&assets_sc64_logo_640_240_dimmed));
version_print();
display_printf("[ Watchdog timeout ]\n");
display_printf("SC64 bootloader did not finish loading in 5 seconds\n");
while (true);
}
if (interrupts != INTERRUPT_NONE) {
display_init((uint32_t *) (&assets_sc64_logo_640_240_dimmed));
version_print();
display_printf("[ Unhandled interrupt(s) ]\n");
display_printf("Pending (0x%02X):\n", interrupts);
for (int i = 0; i < 8; i++) {
switch (interrupts & (1 << i)) {
case INTERRUPT_SW_0: display_printf(" (0) Software interrupt\n"); break;
case INTERRUPT_SW_1: display_printf(" (1) Software interrupt\n"); break;
case INTERRUPT_RCP: display_printf(" (2) RCP interrupt\n"); break;
case INTERRUPT_CART: display_printf(" (3) CART interrupt\n"); break;
case INTERRUPT_PRENMI: display_printf(" (4) Pre NMI interrupt\n"); break;
case INTERRUPT_HW_5: display_printf(" (5) Hardware interrupt\n"); break;
case INTERRUPT_HW_6: display_printf(" (6) Hardware interrupt\n"); break;
case INTERRUPT_TIMER: display_printf(" (7) Timer interrupt\n"); break;
default: break;
}
}
while (true);
}
}

View File

@ -0,0 +1,22 @@
#ifndef INTERRUPTS_H__
#define INTERRUPTS_H__
#include <stdint.h>
#define WITH_INTERRUPTS_DISABLED(x) { \
uint32_t __sr = interrupts_disable(); \
{ x } \
interrupts_restore(__sr); \
}
void interrupts_init (void);
uint32_t interrupts_disable (void);
void interrupts_restore (uint32_t sr);
void interrupts_start_watchdog (void);
void interrupts_stop_watchdog (void);
#endif

View File

@ -1,8 +1,9 @@
#include "interrupts.h"
#include "io.h" #include "io.h"
#include "vr4300.h" #include "vr4300.h"
static void cache_operation (uint8_t operation, uint8_t line_size, void *address, size_t length) { static inline void cache_operation (const uint8_t operation, uint8_t line_size, void *address, size_t length) {
uint32_t cache_address = (((uint32_t) (address)) & (~(line_size - 1))); uint32_t cache_address = (((uint32_t) (address)) & (~(line_size - 1)));
while (cache_address < ((uint32_t) (address) + length)) { while (cache_address < ((uint32_t) (address) + length)) {
asm volatile ( asm volatile (
@ -11,7 +12,7 @@ static void cache_operation (uint8_t operation, uint8_t line_size, void *address
[cache_address] "r" (cache_address) [cache_address] "r" (cache_address)
); );
cache_address += line_size; cache_address += line_size;
} }
} }
void cache_data_hit_writeback_invalidate (void *address, size_t length) { void cache_data_hit_writeback_invalidate (void *address, size_t length) {
@ -72,26 +73,63 @@ uint32_t pi_busy (void) {
} }
uint32_t pi_io_read (io32_t *address) { uint32_t pi_io_read (io32_t *address) {
return cpu_io_read(address); uint32_t value;
WITH_INTERRUPTS_DISABLED({
while (pi_busy());
value = cpu_io_read(address);
});
return value;
} }
void pi_io_write (io32_t *address, uint32_t value) { void pi_io_write (io32_t *address, uint32_t value) {
cpu_io_write(address, value); WITH_INTERRUPTS_DISABLED({
while (pi_busy()); while (pi_busy());
cpu_io_write(address, value);
});
} }
void pi_dma_read (io32_t *address, void *buffer, size_t length) { void pi_dma_read (io32_t *address, void *buffer, size_t length) {
cache_data_hit_writeback_invalidate(buffer, length); cache_data_hit_writeback_invalidate(buffer, length);
cpu_io_write(&PI->PADDR, (uint32_t) (PHYSICAL(address))); WITH_INTERRUPTS_DISABLED({
cpu_io_write(&PI->MADDR, (uint32_t) (PHYSICAL(buffer))); while (pi_busy());
cpu_io_write(&PI->WDMA, length - 1); cpu_io_write(&PI->PADDR, (uint32_t) (PHYSICAL(address)));
cpu_io_write(&PI->MADDR, (uint32_t) (PHYSICAL(buffer)));
cpu_io_write(&PI->WDMA, length - 1);
});
while (pi_busy()); while (pi_busy());
} }
void pi_dma_write (io32_t *address, void *buffer, size_t length) { void pi_dma_write (io32_t *address, void *buffer, size_t length) {
cache_data_hit_writeback(buffer, length); cache_data_hit_writeback(buffer, length);
cpu_io_write(&PI->PADDR, (uint32_t) (PHYSICAL(address))); WITH_INTERRUPTS_DISABLED({
cpu_io_write(&PI->MADDR, (uint32_t) (PHYSICAL(buffer))); while (pi_busy());
cpu_io_write(&PI->RDMA, length - 1); cpu_io_write(&PI->PADDR, (uint32_t) (PHYSICAL(address)));
cpu_io_write(&PI->MADDR, (uint32_t) (PHYSICAL(buffer)));
cpu_io_write(&PI->RDMA, length - 1);
});
while (pi_busy()); while (pi_busy());
} }
uint32_t si_busy (void) {
return (cpu_io_read(&SI->SR) & (SI_SR_IO_BUSY | SI_SR_DMA_BUSY));
}
void si_dma_read (void *buffer) {
cache_data_hit_writeback_invalidate(buffer, PIF_RAM_LENGTH);
WITH_INTERRUPTS_DISABLED({
while (si_busy());
cpu_io_write(&SI->MADDR, (uint32_t) (PHYSICAL(buffer)));
cpu_io_write(&SI->RDMA, (uint32_t) (PIF_RAM));
});
while (si_busy());
}
void si_dma_write (void *buffer) {
cache_data_hit_writeback(buffer, PIF_RAM_LENGTH);
WITH_INTERRUPTS_DISABLED({
while (si_busy());
cpu_io_write(&SI->MADDR, (uint32_t) (PHYSICAL(buffer)));
cpu_io_write(&SI->WDMA, (uint32_t) (PIF_RAM));
});
while (si_busy());
}

View File

@ -202,6 +202,24 @@ typedef struct {
#define PI_SR_CLR_INTR (1 << 1) #define PI_SR_CLR_INTR (1 << 1)
typedef struct {
io32_t MADDR;
io32_t RDMA;
io32_t __reserved_0[2];
io32_t WDMA;
io32_t __reserved_1[1];
io32_t SR;
} si_regs_t;
#define SI_BASE (0x04800000UL)
#define SI ((si_regs_t *) SI_BASE)
#define SI_SR_DMA_BUSY (1 << 0)
#define SI_SR_IO_BUSY (1 << 1)
#define SI_SR_DMA_ERROR (1 << 3)
#define SI_SR_INTERRUPT (1 << 12)
#define ROM_DDIPL_BASE (0x06000000UL) #define ROM_DDIPL_BASE (0x06000000UL)
#define ROM_DDIPL ((io32_t *) ROM_DDIPL_BASE) #define ROM_DDIPL ((io32_t *) ROM_DDIPL_BASE)
@ -210,6 +228,14 @@ typedef struct {
#define ROM_CART ((io32_t *) ROM_CART_BASE) #define ROM_CART ((io32_t *) ROM_CART_BASE)
#define PIF_RAM_BASE (0x1FC007C0)
#define PIF_RAM_LENGTH (64)
#define PIF_RAM ((io32_t *) PIF_RAM_BASE)
void cache_data_hit_writeback_invalidate (void *address, size_t length);
void cache_data_hit_writeback (void *address, size_t length);
void cache_inst_hit_invalidate (void *address, size_t length);
uint32_t c0_count (void); uint32_t c0_count (void);
void delay_ms (int ms); void delay_ms (int ms);
uint32_t cpu_io_read (io32_t *address); uint32_t cpu_io_read (io32_t *address);
@ -220,9 +246,9 @@ uint32_t pi_io_read (io32_t *address);
void pi_io_write (io32_t *address, uint32_t value); void pi_io_write (io32_t *address, uint32_t value);
void pi_dma_read (io32_t *address, void *buffer, size_t length); void pi_dma_read (io32_t *address, void *buffer, size_t length);
void pi_dma_write (io32_t *address, void *buffer, size_t length); void pi_dma_write (io32_t *address, void *buffer, size_t length);
void cache_data_hit_writeback_invalidate (void *address, size_t length); uint32_t si_busy (void);
void cache_data_hit_writeback (void *address, size_t length); void si_dma_read (void *buffer);
void cache_inst_hit_invalidate (void *address, size_t length); void si_dma_write (void *buffer);
#endif #endif

169
sw/bootloader/src/joybus.c Normal file
View File

@ -0,0 +1,169 @@
#include <string.h>
#include "io.h"
#include "joybus.h"
#define PIF_ESCAPE_SKIP_CHANNEL (0x00)
#define PIF_ESCAPE_END_OF_FRAME (0xFE)
#define PIF_COMMAND_JOYBUS_START (1 << 0)
#define JOYBUS_CMD_INFO (0x00)
#define JOYBUS_CMD_STATE (0x01)
#define JOYBUS_CMD_RESET (0xFF)
typedef struct __attribute__((packed)) {
struct __attribute__((packed)) {
uint8_t skip : 1;
uint8_t reset : 1;
uint8_t length : 6;
} tx;
struct __attribute__((packed)) {
uint8_t no_device : 1;
uint8_t timeout : 1;
uint8_t length : 6;
} rx;
uint8_t cmd;
} joybus_trx_info_t;
typedef struct __attribute__((packed)) {
joybus_trx_info_t info;
struct __attribute__((packed)) {
uint16_t id;
uint8_t flags;
} rx;
} joybus_cmd_info_t;
typedef struct __attribute__((packed)) {
joybus_trx_info_t info;
struct __attribute__((packed)) {
uint8_t a : 1;
uint8_t b : 1;
uint8_t z : 1;
uint8_t start : 1;
uint8_t up : 1;
uint8_t down : 1;
uint8_t left : 1;
uint8_t right : 1;
uint8_t reset : 1;
uint8_t __unused : 1;
uint8_t l : 1;
uint8_t r : 1;
uint8_t c_up : 1;
uint8_t c_down : 1;
uint8_t c_left : 1;
uint8_t c_right : 1;
int8_t x;
int8_t y;
} rx;
} joybus_cmd_state_t;
static void joybus_clear_buffer (uint8_t *buffer) {
for (int i = 0; i < PIF_RAM_LENGTH; i++) {
buffer[i] = 0;
}
}
static void joybus_set_starting_channel (uint8_t **ptr, int channel) {
for (int i = 0; i < channel; i++) {
**ptr = PIF_ESCAPE_SKIP_CHANNEL;
*ptr += 1;
}
}
static uint8_t *joybus_append_command (uint8_t **ptr, void *cmd, size_t length) {
((joybus_trx_info_t *) (cmd))->tx.length += 1;
uint8_t *cmd_ptr = *ptr;
memcpy(cmd_ptr, cmd, length);
*ptr += length;
return cmd_ptr;
}
static void joybus_load_result (uint8_t *cmd_ptr, void *cmd, size_t length) {
memcpy(cmd, cmd_ptr, length);
}
static void joybus_finish_frame (uint8_t **ptr) {
**ptr = PIF_ESCAPE_END_OF_FRAME;
*ptr += 1;
}
static void joybus_perform_transaction (uint8_t *buffer) {
buffer[PIF_RAM_LENGTH - 1] = PIF_COMMAND_JOYBUS_START;
si_dma_write(buffer);
si_dma_read(buffer);
}
static bool joybus_device_present (joybus_trx_info_t *trx_info) {
return !(trx_info->rx.no_device || trx_info->rx.timeout);
}
static bool joybus_execute_command (int port, void *cmd, size_t length) {
uint8_t buffer[PIF_RAM_LENGTH] __attribute__((aligned(8)));
uint8_t *ptr = buffer;
joybus_clear_buffer(buffer);
joybus_set_starting_channel(&ptr, port);
uint8_t *cmd_ptr = joybus_append_command(&ptr, cmd, length);
joybus_finish_frame(&ptr);
joybus_perform_transaction(buffer);
joybus_load_result(cmd_ptr, cmd, length);
return joybus_device_present((joybus_trx_info_t *) (cmd_ptr));
}
static void joybus_copy_controller_info (joybus_cmd_info_t *cmd, joybus_controller_info_t *info) {
info->id = cmd->rx.id;
info->flags = cmd->rx.flags;
}
static void joybus_copy_controller_state (joybus_cmd_state_t *cmd, joybus_controller_state_t *state) {
state->a = cmd->rx.a;
state->b = cmd->rx.b;
state->z = cmd->rx.z;
state->start = cmd->rx.start;
state->up = cmd->rx.up;
state->down = cmd->rx.down;
state->left = cmd->rx.left;
state->right = cmd->rx.right;
state->reset = cmd->rx.reset;
state->l = cmd->rx.l;
state->r = cmd->rx.r;
state->c_up = cmd->rx.c_up;
state->c_down = cmd->rx.c_down;
state->c_left = cmd->rx.c_left;
state->c_right = cmd->rx.c_right;
state->x = cmd->rx.x;
state->y = cmd->rx.y;
}
bool joybus_get_controller_info (int port, joybus_controller_info_t *info, bool reset) {
joybus_cmd_info_t cmd = { .info = {
.cmd = reset ? JOYBUS_CMD_RESET : JOYBUS_CMD_INFO,
.rx = { .length = sizeof(cmd.rx) }
} };
if (joybus_execute_command(port, &cmd, sizeof(cmd))) {
joybus_copy_controller_info(&cmd, info);
return true;
}
return false;
}
bool joybus_get_controller_state (int port, joybus_controller_state_t *state) {
joybus_cmd_state_t cmd = { .info = {
.cmd = JOYBUS_CMD_STATE,
.rx = { .length = sizeof(cmd.rx) }
} };
if (joybus_execute_command(port, &cmd, sizeof(cmd))) {
joybus_copy_controller_state(&cmd, state);
return true;
}
return false;
}

View File

@ -0,0 +1,39 @@
#ifndef JOYBUS_H__
#define JOYBUS_H__
#include <stdbool.h>
#include <stdint.h>
typedef struct {
uint16_t id;
uint8_t flags;
} joybus_controller_info_t;
typedef struct {
bool a;
bool b;
bool z;
bool start;
bool up;
bool down;
bool left;
bool right;
bool reset;
bool l;
bool r;
bool c_up;
bool c_down;
bool c_left;
bool c_right;
int8_t x;
int8_t y;
} joybus_controller_state_t;
bool joybus_get_controller_info (int port, joybus_controller_info_t *info, bool reset);
bool joybus_get_controller_state (int port, joybus_controller_state_t *state);
#endif

View File

@ -3,6 +3,7 @@
#include "error.h" #include "error.h"
#include "init.h" #include "init.h"
#include "io.h" #include "io.h"
#include "joybus.h"
#include "menu.h" #include "menu.h"
#include "sc64.h" #include "sc64.h"
@ -12,7 +13,7 @@ void main (void) {
sc64_boot_params_t sc64_boot_params; sc64_boot_params_t sc64_boot_params;
if ((error = sc64_get_boot_params(&sc64_boot_params)) != SC64_OK) { if ((error = sc64_get_boot_params(&sc64_boot_params)) != SC64_OK) {
error_display("Could not obtain boot info: %d", error); error_display("Could not obtain boot info\n (%08X) - %s", error, sc64_error_description(error));
} }
boot_params_t boot_params; boot_params_t boot_params;
@ -22,6 +23,20 @@ void main (void) {
boot_params.cic_seed = (sc64_boot_params.cic_seed & 0xFF); boot_params.cic_seed = (sc64_boot_params.cic_seed & 0xFF);
boot_params.detect_cic_seed = (sc64_boot_params.cic_seed == CIC_SEED_AUTO); boot_params.detect_cic_seed = (sc64_boot_params.cic_seed == CIC_SEED_AUTO);
switch (sc64_boot_params.boot_mode) {
case BOOT_MODE_ROM:
case BOOT_MODE_DDIPL: {
joybus_controller_state_t controller_state;
if (joybus_get_controller_state(0, &controller_state) && controller_state.r) {
sc64_boot_params.boot_mode = BOOT_MODE_MENU;
}
break;
}
default:
break;
}
switch (sc64_boot_params.boot_mode) { switch (sc64_boot_params.boot_mode) {
case BOOT_MODE_MENU: case BOOT_MODE_MENU:
menu_load(); menu_load();
@ -40,7 +55,7 @@ void main (void) {
break; break;
default: default:
error_display("Unknown boot mode selected [%d]\n", sc64_boot_params.boot_mode); error_display("Unknown boot mode selected (%d)\n", sc64_boot_params.boot_mode);
break; break;
} }

View File

@ -8,46 +8,68 @@
#define ROM_ADDRESS (0x10000000) #define ROM_ADDRESS (0x10000000)
extern sc64_error_t sc64_error_fatfs;
static const char *fatfs_error_codes[] = { static const char *fatfs_error_codes[] = {
"Succeeded", "No error",
"A hard error occurred in the low level disk I/O layer", "A hard error occurred in the low level disk I/O layer",
"Assertion failed", "Assertion failed",
"The physical drive cannot work", "The physical drive does not work",
"Could not find the file", "Could not find the file",
"Could not find the path", "Could not find the path",
"The path name format is invalid", "The path name format is invalid",
"Access denied due to prohibited access or directory full", "Access denied due to a prohibited access or directory full",
"Access denied due to prohibited access", "Access denied due to a prohibited access",
"The file/directory object is invalid", "The file/directory object is invalid",
"The physical drive is write protected", "The physical drive is write protected",
"The logical drive number is invalid", "The logical drive number is invalid",
"The volume has no work area", "The volume has no work area",
"There is no valid FAT volume", "Could not find a valid FAT volume",
"The f_mkfs() aborted due to any problem", "The f_mkfs function aborted due to some problem",
"Could not get a grant to access the volume within defined period", "Could not take control of the volume within defined period",
"The operation is rejected according to the file sharing policy", "The operation is rejected according to the file sharing policy",
"LFN working buffer could not be allocated", "LFN working buffer could not be allocated or given buffer is insufficient in size",
"Number of open files > FF_FS_LOCK", "Number of open files > FF_FS_LOCK",
"Given parameter is invalid", "Given parameter is invalid",
}; };
#define FF_CHECK(x, message, ...) { \ static void menu_fix_file_size (FIL *fil) {
fresult = x; \ fil->obj.objsize = ALIGN(f_size(fil), FF_MAX_SS);
if (fresult != FR_OK) { \ }
error_display(message ".\nReason: %s", __VA_ARGS__ __VA_OPT__(,) fatfs_error_codes[fresult]); \
} \ static void menu_error_display (const char *message, FRESULT fresult) {
error_display(
" > FatFs error: %s\n"
" > SD card error: %s (%08X)\n"
"\n"
"[ %s ]\n"
" > Please insert a FAT32/exFAT formatted SD card.\n"
" > To start the menu please put \"sc64menu.n64\" file\n"
" in the top directory on the SD card.\n"
" > Latest menu version is available on the\n"
" https://menu.summercart64.dev website.\n",
fatfs_error_codes[fresult],
sc64_error_description(sc64_error_fatfs),
sc64_error_fatfs,
message
);
} }
static void fix_menu_file_size (FIL *fil) { #define FF_CHECK(x, message) { \
fil->obj.objsize = ALIGN(f_size(fil), FF_MAX_SS); fresult = x; \
if (fresult != FR_OK) { \
menu_error_display(message, fresult); \
} \
} }
void menu_load (void) { void menu_load (void) {
sc64_error_t error; sc64_error_t error;
bool writeback_pending; bool writeback_pending;
sc64_sd_card_status_t sd_card_status;
FRESULT fresult; FRESULT fresult;
FATFS fs; FATFS fs;
FIL fil; FIL fil;
@ -55,17 +77,25 @@ void menu_load (void) {
do { do {
if ((error = sc64_writeback_pending(&writeback_pending)) != SC64_OK) { if ((error = sc64_writeback_pending(&writeback_pending)) != SC64_OK) {
error_display("Command WRITEBACK_PENDING failed: %d", error); error_display("Command WRITEBACK_PENDING failed\n (%08X) - %s", error, sc64_error_description(error));
} }
} while (writeback_pending); } while (writeback_pending);
if ((error = sc64_writeback_disable()) != SC64_OK) { if ((error = sc64_writeback_disable()) != SC64_OK) {
error_display("Could not disable writeback: %d", error); error_display("Could not disable save writeback\n (%08X) - %s", error, sc64_error_description(error));
} }
FF_CHECK(f_mount(&fs, "", 1), "SD card initialize error. No SD card or invalid partition table"); if ((error = sc64_sd_card_get_status(&sd_card_status)) != SC64_OK) {
FF_CHECK(f_open(&fil, "sc64menu.n64", FA_READ), "Could not open menu executable (sc64menu.n64)"); error_display("Could not get SD card status\n (%08X) - %s", error, sc64_error_description(error));
fix_menu_file_size(&fil); }
if (!(sd_card_status & SD_CARD_STATUS_INSERTED)) {
menu_error_display("No SD card detected in the slot", FR_OK);
}
FF_CHECK(f_mount(&fs, "", 1), "SD card initialize error");
FF_CHECK(f_open(&fil, "sc64menu.n64", FA_READ), "Could not open menu executable");
menu_fix_file_size(&fil);
FF_CHECK(f_read(&fil, (void *) (ROM_ADDRESS), f_size(&fil), &bytes_read), "Could not read menu file"); FF_CHECK(f_read(&fil, (void *) (ROM_ADDRESS), f_size(&fil), &bytes_read), "Could not read menu file");
FF_CHECK((bytes_read != f_size(&fil)) ? FR_INT_ERR : FR_OK, "Read size is different than expected"); FF_CHECK((bytes_read != f_size(&fil)) ? FR_INT_ERR : FR_OK, "Read size is different than expected");
FF_CHECK(f_close(&fil), "Could not close menu file"); FF_CHECK(f_close(&fil), "Could not close menu file");

Some files were not shown because too many files have changed in this diff Show More