diff --git a/CMakeLists.txt b/CMakeLists.txt index ca3b2fc..dfcc3d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,12 +8,15 @@ message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") set (CMAKE_CXX_FLAGS "-static-libgcc -static-libstdc++ -Werror -Wall -Wunused-result -Wno-unused-function -Wno-unused-variable -fno-omit-frame-pointer -fsanitize=address -Wno-c99-designator") +find_package(SDL2 REQUIRED) + add_library(vm_lib) target_sources(vm_lib PRIVATE src/vm.cpp src/debug.cpp src/elf.cpp + src/framebuffer.cpp PUBLIC FILE_SET HEADERS @@ -26,5 +29,6 @@ target_sources(vm_lib add_executable(rve src/rve.cpp) target_link_libraries(rve vm_lib) +target_link_libraries(rve ${SDL2_LIBRARIES}) install(TARGETS rve) diff --git a/example/Makefile b/example/Makefile index 001e78d..7fe325a 100644 --- a/example/Makefile +++ b/example/Makefile @@ -2,5 +2,5 @@ example: example.c Makefile boot.s linker.ld printf.h printf.c putchar.c riscv32-none-elf-as -march=rv32i -mabi=ilp32 boot.s -o boot.o riscv32-none-elf-gcc -fno-builtin -fvisibility=hidden -nostdlib -nostartfiles -march=rv32im -mabi=ilp32 -D PRINTF_DISABLE_SUPPORT_FLOAT=1 -D PRINTF_DISABLE_SUPPORT_LONG_LONG=1 -c printf.c -o printf.o -g riscv32-none-elf-gcc -fno-builtin -fvisibility=hidden -nostdlib -nostartfiles -march=rv32im -mabi=ilp32 -c putchar.c -o putchar.o -g - riscv32-none-elf-gcc -fno-builtin -fvisibility=hidden -nostdlib -nostartfiles -march=rv32im -mabi=ilp32 -c example.c -o example.o -g - riscv32-none-elf-ld boot.o example.o printf.o putchar.o -T linker.ld -o example -g + riscv32-none-elf-gcc -fno-builtin -fvisibility=hidden -nostdlib -nostartfiles -march=rv32im -mabi=ilp32 -c example.c -o example.o -g -O2 + riscv32-none-elf-ld boot.o example.o printf.o putchar.o -T linker.ld -o example -g -O2 diff --git a/example/example.c b/example/example.c index b2d7eda..ac8f9bd 100644 --- a/example/example.c +++ b/example/example.c @@ -4,13 +4,18 @@ typedef unsigned long long uint64_t; typedef unsigned long uint32_t; #define MTIME_BASE 0x0200BFF8 +volatile uint32_t* mtime_upper = (uint32_t*)(MTIME_BASE+4); +volatile uint32_t* mtime_lower = (uint32_t*)MTIME_BASE; + +#define FRAMEBUFFER_BASE 0x1D385000 + uint64_t read_mtime_atomic() { uint32_t upper1, lower, upper2; do { - upper1 = *(uint32_t*)(MTIME_BASE + 4); - lower = *(uint32_t*)(MTIME_BASE); - upper2 = *(uint32_t*)(MTIME_BASE + 4); + upper1 = *mtime_upper; + lower = *mtime_lower; + upper2 = *mtime_upper; } while (upper1 != upper2); // Repeat if upper changed during the process return ((uint64_t)upper1 << 32) | lower; @@ -29,11 +34,26 @@ int fib(int n) { return fib(n-1) + fib(n-2); } +void draw() { + for (int i = 0; i< 640; i++) { + for (int j = 0; j < 480; j++) { + uint32_t color = i*j; + uint32_t* addr = (uint32_t*)(FRAMEBUFFER_BASE + ((j*640) + i) *4); + *addr = color; + } + } +} + int main() { - int n = 24; uint64_t start = read_mtime_atomic(); - int res = fib(n); + + draw(); + uint64_t end = read_mtime_atomic(); + + int n = 24; + int res = fib(n); + uint32_t total_msec = timediff_ms(start, end); printf("fib(%d) = %d\n", n, res); diff --git a/rve.nix b/rve.nix index 35bfa8a..d4efb8c 100644 --- a/rve.nix +++ b/rve.nix @@ -18,6 +18,7 @@ pkgs.gcc13Stdenv.mkDerivation rec { linuxPackages.perf jq lcov + (SDL2.overrideAttrs {waylandSupport= true;}) #riscv-pkgs.buildPackages.gcc pkgsCross.riscv32-embedded.buildPackages.gcc pkgsCross.riscv32-embedded.buildPackages.binutils diff --git a/src/framebuffer.cpp b/src/framebuffer.cpp new file mode 100644 index 0000000..a5127af --- /dev/null +++ b/src/framebuffer.cpp @@ -0,0 +1,61 @@ +#include +#include + +#include + +#include "vm.hpp" + +SDL_Window* window = nullptr; +SDL_Renderer* renderer = nullptr; +SDL_Texture* texture = nullptr; + +Framebuffer::Framebuffer() : Device(FRAMEBUFFER_ADDR, 640 * 480 * 4) { + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + throw std::runtime_error("Failed to initialize SDL"); + } + window = SDL_CreateWindow("rve Framebuffer", SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, 640, 480, 0); + if (!window) { + throw std::runtime_error("Failed to create SDL window"); + } + /* + SDL_Surface* surface = SDL_GetWindowSurface(window); + if (!surface) { + throw std::runtime_error("Failed to create SDL surface"); + } + */ + + // renderer = SDL_CreateSoftwareRenderer(surface); + + // renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE); + if (!renderer) { + throw std::runtime_error("Failed to create SDL renderer"); + } + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, 640, 480); + if (!texture) { + throw std::runtime_error("Failed to create SDL texture"); + } + + SDL_ShowWindow(window); + + draw(); +} + +void Framebuffer::draw() { + SDL_UpdateTexture(texture, nullptr, &mem[0], + 640 * 4); // Update the SDL texture + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, nullptr, nullptr); + SDL_RenderPresent(renderer); +} + +void Framebuffer::write_mem(uint8_t* src, size_t addr, size_t size) { + Device::write_mem(src, addr, size); + + if (addr + size == base() + (640 * 480) * 4) { + // draw to the screen when the last byte is written + draw(); + } +} diff --git a/src/vm.cpp b/src/vm.cpp index 94d210c..a0b83d4 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -15,10 +15,11 @@ inline int32_t sign_extend(int32_t value, int bits) { VM::VM(const std::vector& memory, const std::string& file_path) : ram(memory), pc(PROGRAM_ADDR), file_path(file_path) { - devices = std::vector(3, 0); + devices = std::vector(4, 0); devices[0] = &ram; devices[1] = &uart; devices[2] = &timer; + devices[3] = &framebuffer; } void VM::setreg(size_t regnum, uint32_t value) { diff --git a/src/vm.hpp b/src/vm.hpp index d73c5d3..34e96b4 100644 --- a/src/vm.hpp +++ b/src/vm.hpp @@ -12,6 +12,7 @@ const int NUM_REGISTERS = 32; // Standard RISC-V has 32 registers const uint32_t UART_ADDR = 0x10000000; const uint32_t PROGRAM_ADDR = 0x80000000; const uint32_t MTIME_ADDR = 0x0200BFF8; +const uint32_t FRAMEBUFFER_ADDR = 0x1D385000; class Device { public: @@ -28,7 +29,7 @@ class Device { uint32_t base() { return base_addr; } uint32_t size() { return mem_size; } - private: + protected: uint32_t base_addr = 0; uint32_t mem_size = 0; std::vector mem; @@ -64,6 +65,15 @@ class Timer final : public Device { void set(uint64_t value) { write_mem((uint8_t *)&value, MTIME_ADDR, 8); } }; +class Framebuffer final : public Device { + public: + Framebuffer(); + virtual void write_mem(uint8_t *src, size_t addr, size_t size); + + protected: + void draw(); +}; + class VM { public: VM(const std::vector &memory, const std::string &file_path); @@ -90,5 +100,6 @@ class VM { UART uart; Timer timer; + Framebuffer framebuffer; std::vector devices; };