rv51 is a personal project of @cyrozap. This means that @cyrozap works on this project for fun, in his free time, on his own schedule, and in the direction he chooses. To this end, @cyrozap reserves the right to ignore or reject issues, pull requests, and requests for support, for any reason or no reason at all.
rv51 is also a Free Software project. That means anyone can use rv51 for any purpose, can study and modify it, redistribute it, and distribute modified copies of it. So if you'd like to use rv51 for some particular purpose, would like to change it to meet some need of yours, or simply want to share it with others, so long as you follow the terms of the license you have every right to do so. You don't need any extra permission to make changes in your own fork of the project, whether that fork exists only privately on your computer or if it has been posted publicly on the internet.
That said, if you intend for your changes to be included in @cyrozap's fork of rv51, please open an issue to ask if those changes would be merged before you perform any work. If you don't ask first and just open a pull request once you've finished your work, there's a good chance that the pull request will be rejected. As rv51 is an assembly-language project, it's critical for the maintenance of the project that the code style and design/architecture be kept consistent. Since every new feature will have some impact on code size or performance, and "simply" making features configurable makes testing much more difficult (every new feature flag doubles the number of possible feature combinations), new features (like the C extension, M mode, and performance counters) and their designs must be considered carefully before they are implemented. It's for this reason that you're asked to open an issue first if you have an idea for a change--this way, the design and tradeoffs can be discussed before any work is done, reducing the likelihood of the change being rejected.
- Check the Project scope to make sure your idea is either in-scope or at least is not out-of-scope.
- Search through both open and closed issues to make sure that what you're about to ask hasn't already been asked about.
- Open an issue in this repository explaining the kind of change you would like to make.
- @cyrozap will inform you in the issue of any next steps to take.
- Follow the "Rules and conventions" in main.S unless it's necessary to
break them.
- If a rule/convention needs to be broken, note it in a comment near the relevant code.
- If you need internal memory for this feature, use the register mapping
spreadsheet to help plan the allocation.
- Take memory from the stack space first, unless the memory will need some special kind of access (e.g., for direct access to bits).
- Avoid pushing data to the stack except where necessary (e.g., to temporarily
save
DPTR
before loading the address of a jump table). - Comment your code extensively, as if you'll wake up one day having no memory
of ever having written that code.
- Explain what registers are being used for what variables, why you're making the calculations you're making--those kinds of things.
- See the existing comments in the code to get a better idea of what code should or should not have comments.
Some suggestions for implementation are included.
- RISC-V C-extension (compressed instructions)
- To start, all compressed instructions must be decompressed into 4-byte instructions before returning to the standard instruction emulation code.
- Optimizations (such as skipping decompression steps, pre-decoding before jumping directly to emulation code, and performing the instruction emulation directly while decoding the compressed instruction) can be made after the initial, less-optimized support is added.
- RISC-V M-mode (privileged architecture)
- Performance Monitor
minstret
CSRmcounteren
CSRmcountinhibit
CSR
- Performance Monitor
This is not an exhaustive list, but it should at least give you an idea of the kinds of functionality that will probably never be included in rv51.
- RISC-V RV64 instruction set (64-bit operations) and F/D/Q/L-extensions
(single-precision/double-precision/quad-precision/decimal floating point)
- Support for RV64I would take up more internal memory than is available in
the 8051. The registers themselves would require at least 248 bytes of
internal data memory in total, leaving only 8 bytes for a stack and other
emulator state, which is not nearly enough.
- In theory, the registers could be stored in XDATA-attached RAM, but the presence, size, and base address of this RAM is platform-dependent and would require a significant rewrite of rv51.
- The "F" Standard Extension adds 32 32-bit floating point registers, plus a 32-bit floating point control and status register, so at least 128 additional bytes of internal data memory would be required to store those registers. And the other floating point extensions would require even more memory.
- 64-bit operations are extremely slow on the 8051. Assuming there was enough space somewhere for the 248-byte register file, 64-bit operations would take about twice as long for the 8051 to emulate as the equivalent 32-bit instructions while providing little-to-no benefit on common microcontroller tasks.
- The marginal gains of implementing those instruction sets is far outweighed
by the marginal cost of implementing them, in terms of how much data and
code memory would be required.
- Going from "not being able to execute any RISC-V instructions at all" to
"being able to execute any RV32I instruction" is a huge leap in
functionality. And in addition to significantly speeding up multiply and
divide operations, support for the "M" extension is required in order to
use Rust toolchains that emit compressed instructions
(
riscv32imc-unknown-none-elf
andriscv32imac-unknown-none-elf
). Compared to those, floating point and 64-bit support are simultaneously much more costly to implement and much less useful than those other extensions, at least for applications where an 8051 might be used.
- Going from "not being able to execute any RISC-V instructions at all" to
"being able to execute any RV32I instruction" is a huge leap in
functionality. And in addition to significantly speeding up multiply and
divide operations, support for the "M" extension is required in order to
use Rust toolchains that emit compressed instructions
(
- Support for RV64I would take up more internal memory than is available in
the 8051. The registers themselves would require at least 248 bytes of
internal data memory in total, leaving only 8 bytes for a stack and other
emulator state, which is not nearly enough.
- Bootloader to dynamically load RISC-V code
- There are simply too many potential platforms and use cases to make supporting this feasible. For example, many 8051-based microcontrollers use the standard 8051 serial port peripheral, but some use memory-mapped 8250-style UARTs in XDATA space. And while many users might want to load code over serial, others might want to load it over SPI, I2C, or even USB or PCIe, depending on what peripherals are available on their target microcontroller and their target application.
- Porting to specific platforms (e.g., adding support for non-standard
peripherals in SFR or XDATA space, extended addressing modes, firmware
headers, etc.)
- There are simply too many platforms to support, and making the build configurable would make it very difficult to test everything due to the large number of possible configuration combinations.