diff options
| author | Alejandro Soto <alejandro@34project.org> | 2022-11-14 21:10:40 -0600 |
|---|---|---|
| committer | Alejandro Soto <alejandro@34project.org> | 2022-11-14 21:10:40 -0600 |
| commit | 6fb3849e73b797d4610a2b782127f927dec0c9c9 (patch) | |
| tree | 9d17de8907d860b795761e0644f17d0fd33106de | |
| parent | cad870295dfb741d5c24c25016c5bba878bc37e5 (diff) | |
Implement VGA simulation
Diffstat (limited to '')
| -rw-r--r-- | tb/avalon.hpp | 56 | ||||
| -rw-r--r-- | tb/avalon.impl.hpp | 22 | ||||
| -rw-r--r-- | tb/mem.cpp | 55 | ||||
| -rw-r--r-- | tb/mem.hpp | 42 | ||||
| -rw-r--r-- | tb/mem.impl.hpp | 81 | ||||
| -rw-r--r-- | tb/null.hpp | 31 | ||||
| -rw-r--r-- | tb/platform.sv | 8 | ||||
| -rwxr-xr-x | tb/sim/sim.py | 2 | ||||
| -rw-r--r-- | tb/top/conspiracion.cpp | 35 | ||||
| -rw-r--r-- | tb/vga.hpp | 72 | ||||
| -rw-r--r-- | tb/vga.impl.hpp | 356 | ||||
| -rw-r--r-- | tb/vga_domain.sv | 35 | ||||
| -rw-r--r-- | tb/window.hpp | 37 |
13 files changed, 727 insertions, 105 deletions
diff --git a/tb/avalon.hpp b/tb/avalon.hpp index a00ced0..8134677 100644 --- a/tb/avalon.hpp +++ b/tb/avalon.hpp @@ -1,7 +1,9 @@ -#ifndef AVALON_HPP -#define AVALON_HPP +#ifndef TALLER_AVALON_HPP +#define TALLER_AVALON_HPP +#include <cassert> #include <cstdint> +#include <cstdio> #include <vector> namespace taller::avalon @@ -9,11 +11,57 @@ namespace taller::avalon class slave { public: - virtual std::uint32_t base_address() noexcept = 0; - virtual std::uint32_t address_mask() noexcept = 0; + inline slave(std::uint32_t base, std::uint32_t size, std::size_t word_size) + : base(base), + mask(~(size - 1)), + word(log2i(word_size)) + { + assert(!((word_size - 1) & word_size)); + assert(!(base & word_mask()) && !(size & word_mask()) && !((size - 1) & size)); + } + + inline std::uint32_t base_address() noexcept + { + return base; + } + + inline std::uint32_t address_mask() noexcept + { + return mask; + } + + inline std::uint32_t word_mask() noexcept + { + return (1 << word) - 1; + } + + inline std::size_t word_size() noexcept + { + return 1 << word; + } + + inline unsigned word_bits() noexcept + { + return word; + } + + inline std::uint32_t address_span() noexcept + { + return ~mask + 1; + } virtual bool read(std::uint32_t addr, std::uint32_t &data) = 0; virtual bool write(std::uint32_t addr, std::uint32_t data, unsigned byte_enable) = 0; + + private: + std::uint32_t base; + std::uint32_t mask; + unsigned word; + + static inline int log2i(int i) + { + return sizeof(int) * 8 - __builtin_clz(i) - 1; + } }; template<class Platform> diff --git a/tb/avalon.impl.hpp b/tb/avalon.impl.hpp index f701e73..03685d0 100644 --- a/tb/avalon.impl.hpp +++ b/tb/avalon.impl.hpp @@ -1,5 +1,5 @@ -#ifndef AVALON_IMPL_HPP -#define AVALON_IMPL_HPP +#ifndef TALLER_AVALON_IMPL_HPP +#define TALLER_AVALON_IMPL_HPP #include <cassert> #include <cstdio> @@ -61,11 +61,9 @@ namespace taller::avalon avl_byteenable = plat.avl_byteenable; assert(!avl_read || !avl_write); - - if(avl_address & 0b11) + if(!avl_read && !avl_write) { - fprintf(stderr, "[avl] unaligned address: 0x%08x\n", avl_address); - assert(false); + return; } for(auto &binding : devices) @@ -83,13 +81,21 @@ namespace taller::avalon fprintf(stderr, "[avl] attempt to %s memory hole at 0x%08x\n", op, avl_address); assert(false); } + + if(avl_address & active->word_mask()) + { + fprintf(stderr, "[avl] unaligned address: 0x%08x\n", avl_address); + assert(false); + } } - auto pos = (avl_address & ~active->address_mask()) >> 2; + auto pos = (avl_address & ~active->address_mask()) >> active->word_bits(); if(avl_read) { - plat.avl_waitrequest = !active->read(pos, plat.avl_readdata); + std::uint32_t readdata; + plat.avl_waitrequest = !active->read(pos, readdata); + plat.avl_readdata = readdata; } else if(avl_write) { plat.avl_waitrequest = !active->write(pos, avl_writedata, avl_byteenable); diff --git a/tb/mem.cpp b/tb/mem.cpp deleted file mode 100644 index 6eeb7df..0000000 --- a/tb/mem.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include <cassert> -#include <cstdint> -#include <memory> - -#include "mem.hpp" - -namespace taller::avalon -{ - mem::mem(std::uint32_t base, std::uint32_t size) - : base(base), mask(~(size - 1)), - block(std::make_unique<std::uint32_t[]>(size >> 2)) - { - assert(!(size & 0b11) && !((size - 1) & size)); - } - - bool mem::read(std::uint32_t addr, std::uint32_t &data) - { - data = block[addr]; - return ready(); - } - - bool mem::write(std::uint32_t addr, std::uint32_t data, unsigned byte_enable) - { - std::uint32_t bytes = 0; - - if(byte_enable & 0b1000) - { - bytes |= 0xff << 24; - } - - if(byte_enable & 0b0100) - { - bytes |= 0xff << 16; - } - - if(byte_enable & 0b0010) - { - bytes |= 0xff << 8; - } - - if(byte_enable & 0b0001) - { - bytes |= 0xff; - } - - block[addr] = (data & bytes) | (block[addr] & ~bytes); - return ready(); - } - - bool mem::ready() noexcept - { - count = count > 0 ? count - 1 : 2; - return count == 0; - } -} @@ -1,5 +1,5 @@ -#ifndef MEM_HPP -#define MEM_HPP +#ifndef TALLER_MEM_HPP +#define TALLER_MEM_HPP #include <cstdint> #include <memory> @@ -8,54 +8,30 @@ namespace taller::avalon { + template<typename Cell> class mem : public slave { public: mem(std::uint32_t base, std::uint32_t size); - virtual inline std::uint32_t base_address() noexcept final override - { - return base; - } - - virtual inline std::uint32_t address_mask() noexcept final override - { - return mask; - } - virtual bool read(std::uint32_t addr, std::uint32_t &data) final override; + virtual bool write ( std::uint32_t addr, std::uint32_t data, unsigned byte_enable = 0b1111 ) final override; template<typename F> - void load(F loader, std::size_t addr = 0); + void load(F loader, std::size_t offset = 0); private: - std::unique_ptr<std::uint32_t[]> block; - std::uint32_t base; - std::uint32_t mask; - unsigned count = 0; + std::unique_ptr<Cell[]> block; + unsigned count = 0; bool ready() noexcept; }; - - template<typename F> - void mem::load(F loader, std::size_t addr) - { - std::size_t size = mask + 1; - while(addr < size) - { - std::size_t read = loader(&block[base + addr], size - addr); - if(read == 0) - { - break; - } - - addr += read; - } - } } +#include "mem.impl.hpp" + #endif diff --git a/tb/mem.impl.hpp b/tb/mem.impl.hpp new file mode 100644 index 0000000..f7bb424 --- /dev/null +++ b/tb/mem.impl.hpp @@ -0,0 +1,81 @@ +#ifndef TALLER_MEM_IMPL_HPP +#define TALLER_MEM_IMPL_HPP + +#include <cassert> +#include <cstdint> +#include <memory> + +namespace taller::avalon +{ + template<typename Cell> + mem<Cell>::mem(std::uint32_t base, std::uint32_t size) + : slave(base, size, sizeof(Cell)), + block(std::make_unique<Cell[]>(size >> word_bits())) + {} + + template<typename Cell> + template<typename F> + void mem<Cell>::load(F loader, std::size_t offset) + { + auto base = base_address(); + auto bits = word_bits(); + std::size_t size = address_span(); + std::size_t addr = base_address() + offset; + + while(addr >= base && addr < base + size) + { + std::size_t read = loader(&block[(addr - base) >> bits], (base + size - addr) >> bits); + if(read == 0) + { + break; + } + + addr += read << bits; + } + } + + template<typename Cell> + bool mem<Cell>::read(std::uint32_t addr, std::uint32_t &data) + { + data = block[addr]; + return ready(); + } + + template<typename Cell> + bool mem<Cell>::write(std::uint32_t addr, std::uint32_t data, unsigned byte_enable) + { + std::uint32_t bytes = 0; + + if(byte_enable & 0b1000) + { + bytes |= 0xff << 24; + } + + if(byte_enable & 0b0100) + { + bytes |= 0xff << 16; + } + + if(byte_enable & 0b0010) + { + bytes |= 0xff << 8; + } + + if(byte_enable & 0b0001) + { + bytes |= 0xff; + } + + block[addr] = (data & bytes) | (block[addr] & ~bytes); + return ready(); + } + + template<typename Cell> + bool mem<Cell>::ready() noexcept + { + count = count > 0 ? count - 1 : 2; + return count == 0; + } +} + +#endif diff --git a/tb/null.hpp b/tb/null.hpp new file mode 100644 index 0000000..37de464 --- /dev/null +++ b/tb/null.hpp @@ -0,0 +1,31 @@ +#ifndef TALLER_NULL_HPP +#define TALLER_NULL_HPP + +#include <cstdint> +#include <memory> + +#include "avalon.hpp" + +namespace taller::avalon +{ + class null : public slave + { + public: + using slave::slave; + + inline virtual bool read(std::uint32_t addr, std::uint32_t &data) final override + { + return true; + } + + inline virtual bool write + ( + std::uint32_t addr, std::uint32_t data, unsigned byte_enable = 0b1111 + ) final override + { + return true; + } + }; +} + +#endif diff --git a/tb/platform.sv b/tb/platform.sv index cbf31b0..5def85f 100644 --- a/tb/platform.sv +++ b/tb/platform.sv @@ -1,6 +1,3 @@ - - - module platform ( input wire clk_clk, // clk.clk @@ -77,4 +74,9 @@ module platform .* ); + vga_domain vga + ( + .* + ); + endmodule diff --git a/tb/sim/sim.py b/tb/sim/sim.py index bef1b73..0c8b023 100755 --- a/tb/sim/sim.py +++ b/tb/sim/sim.py @@ -249,7 +249,7 @@ mem_dumps = module_get('mem_dumps', []) if init := module_get('init'): init() -exec_args = [verilated, '--cycles', str(cycles), '--dump-regs'] +exec_args = [verilated, '--headless', '--cycles', str(cycles), '--dump-regs'] for rng in mem_dumps: length = rng.stop - rng.start diff --git a/tb/top/conspiracion.cpp b/tb/top/conspiracion.cpp index ef9235d..2840b15 100644 --- a/tb/top/conspiracion.cpp +++ b/tb/top/conspiracion.cpp @@ -12,6 +12,7 @@ #include "Vconspiracion_arm810.h" #include "Vconspiracion_conspiracion.h" #include "Vconspiracion_platform.h" +#include "Vconspiracion_vga_domain.h" #include "Vconspiracion_core_control.h" #include "Vconspiracion_core_psr.h" #include "Vconspiracion_core_regs.h" @@ -21,6 +22,9 @@ #include "../avalon.hpp" #include "../mem.hpp" +#include "../null.hpp" +#include "../window.hpp" +#include "../vga.hpp" namespace { @@ -113,6 +117,7 @@ namespace int main(int argc, char **argv) { using namespace taller::avalon; + using namespace taller::vga; Verilated::commandArgs(argc, argv); @@ -138,6 +143,11 @@ int main(int argc, char **argv) parser, "dump-regs", "Dump all registers", {"dump-regs"} ); + args::Flag headless + ( + parser, "headless", "Disable video output", {"headless"} + ); + args::ValueFlag<unsigned> cycles ( parser, "cycles", "Number of core cycles to run", {"cycles"}, 256 @@ -184,10 +194,27 @@ int main(int argc, char **argv) } interconnect<Vconspiracion_platform> avl(*top.conspiracion->plat); - mem hps_ddr3(0x0000'0000, 512 << 20); + interconnect<Vconspiracion_vga_domain> avl_vga(*top.conspiracion->plat->vga); + + mem<std::uint32_t> hps_ddr3(0x0000'0000, 512 << 20); + mem<std::uint16_t> vram(0x3800'0000, 64 << 20); + null vram_null(0x3800'0000, 64 << 20, 2); + window vram_window(vram, 0x0000'0000); + display<Vconspiracion_vga_domain> vga(*top.conspiracion->plat->vga, 25'175'000); + + bool enable_video = !headless; avl.attach(hps_ddr3); + if(enable_video) + { + avl.attach(vram); + avl_vga.attach(vram_window); + } else + { + avl.attach(vram_null); + } + FILE *img_file = std::fopen(image->c_str(), "rb"); if(!img_file) { @@ -216,7 +243,13 @@ int main(int argc, char **argv) { top.clk_clk = !top.clk_clk; top.eval(); + avl.tick(top.clk_clk); + if(enable_video) + { + avl_vga.tick(top.clk_clk); + vga.tick(top.clk_clk); + } if(enable_trace) { diff --git a/tb/vga.hpp b/tb/vga.hpp new file mode 100644 index 0000000..92446ca --- /dev/null +++ b/tb/vga.hpp @@ -0,0 +1,72 @@ +#ifndef LIBTALLER_VGA_HPP +#define LIBTALLER_VGA_HPP + +#include <array> +#include <cstddef> +#include <cstdint> + +#include <SDL2/SDL_surface.h> +#include <SDL2/SDL_video.h> + +namespace taller::vga +{ + struct timings + { + unsigned active; + unsigned front_porch; + unsigned sync; + unsigned back_porch; + }; + + struct video_mode + { + std::uint32_t pixel_clk; + timings h; + timings v; + }; + + template<class Crtc> + class display + { + public: + display(Crtc &crtc, std::uint32_t clock_hz) noexcept; + + ~display() noexcept; + + void tick(bool clk) noexcept; + + inline bool key(std::size_t index) + { + return keys.at(index); + } + + private: + Crtc& crtc; + SDL_Window *window = nullptr; + const video_mode *mode = nullptr; + unsigned row = 0; + unsigned col = 0; + unsigned hsync_lo = 0; + unsigned hsync_hi = 0; + unsigned vsync_hi = 0; + unsigned vsync_lo = 0; + bool last_hsync = false; + bool last_vsync = false; + bool found_line = false; + const std::uint32_t clock_hz; + + std::array<bool, 4> keys = {}; + + void move_pos() noexcept; + void scan_syncs() noexcept; + void scan_vsync() noexcept; + void put_pixel() noexcept; + void guess_mode() noexcept; + void signal_lost() noexcept; + void update_window() noexcept; + }; +} + +#include "vga.impl.hpp" + +#endif diff --git a/tb/vga.impl.hpp b/tb/vga.impl.hpp new file mode 100644 index 0000000..9c3294c --- /dev/null +++ b/tb/vga.impl.hpp @@ -0,0 +1,356 @@ +#ifndef TALLER_VGA_IMPL_HPP +#define TALLER_VGA_IMPL_HPP + +#include <array> +#include <cassert> +#include <cmath> +#include <cstdio> +#include <cstdio> + +#include <SDL2/SDL.h> +#include <SDL2/SDL_surface.h> +#include <SDL2/SDL_video.h> + +namespace +{ + // https://web.mit.edu/6.111/www/s2004/NEWKIT/vga.shtml + constexpr std::array<taller::vga::video_mode, 13> MODES + {{ + {25'175'000, { 640, 16, 96, 48}, {480, 11, 2, 31}}, + {31'500'000, { 640, 24, 40, 128}, {480, 9, 3, 28}}, + {31'500'000, { 640, 16, 96, 48}, {480, 11, 2, 32}}, + {36'000'000, { 640, 32, 48, 112}, {480, 1, 3, 25}}, + {38'100'000, { 800, 32, 128, 128}, {600, 1, 4, 14}}, + {40'000'000, { 800, 40, 128, 88}, {600, 1, 4, 23}}, + {50'000'000, { 800, 56, 120, 64}, {600, 37, 6, 23}}, + {49'500'000, { 800, 16, 80, 160}, {600, 1, 2, 21}}, + {56'250'000, { 800, 32, 64, 152}, {600, 1, 3, 27}}, + {65'000'000, {1024, 24, 136, 160}, {768, 3, 6, 29}}, + {75'000'000, {1024, 24, 136, 144}, {768, 3, 6, 29}}, + {78'750'000, {1024, 16, 96, 176}, {768, 1, 3, 28}}, + {94'500'000, {1024, 48, 96, 208}, {768, 1, 3, 36}} + }}; +} + +namespace taller::vga +{ + template<class Crtc> + display<Crtc>::display(Crtc &crtc, std::uint32_t clock_hz) noexcept + : crtc(crtc), + clock_hz(clock_hz) + {} + + template<class Crtc> + display<Crtc>::~display() noexcept + { + mode = nullptr; + update_window(); + } + + template<class Crtc> + void display<Crtc>::tick(bool clk) noexcept + { + if(!clk) + { + return; + } + + put_pixel(); + move_pos(); + scan_syncs(); + + last_hsync = crtc.vga_hsync; + last_vsync = crtc.vga_vsync; + + ::SDL_Event event; + while(::SDL_PollEvent(&event)) + { + bool value; + switch(event.type) + { + case SDL_KEYDOWN: + value = false; + break; + + case SDL_KEYUP: + value = true; + break; + + default: + continue; + } + + std::size_t index; + switch(event.key.keysym.sym) + { + case SDLK_1: + index = 0; + break; + + case SDLK_2: + index = 1; + break; + + case SDLK_3: + index = 2; + break; + + case SDLK_4: + index = 3; + break; + + default: + continue; + } + + keys[index] = value; + } + } + + template<class Crtc> + void display<Crtc>::move_pos() noexcept + { + if(!mode) + { + if(found_line && ++col >= hsync_lo + hsync_hi) + { + col = 0; + } + + return; + } + + auto dots = mode->h.active + mode->h.front_porch + mode->h.sync + mode->h.back_porch; + auto lines = mode->v.active + mode->v.front_porch + mode->v.sync + mode->v.back_porch; + + if((col + 1 != mode->h.sync || (!last_hsync && crtc.vga_hsync)) && ++col >= dots) + { + col = 0; + if(row + 1 != mode->v.sync && ++row >= lines) + { + row = 0; + } + } + + if(!last_vsync && crtc.vga_vsync) + { + ++row; + if(window) + { + ::SDL_UpdateWindowSurface(window); + } + } + } + + template<class Crtc> + void display<Crtc>::scan_syncs() noexcept + { + if(hsync_lo == 0) + { + hsync_lo += last_hsync && !crtc.vga_hsync; + } else if(hsync_hi == 0) + { + if(crtc.vga_hsync) + { + scan_vsync(); + } else + { + ++hsync_lo; + } + } else + { + scan_vsync(); + } + } + + template<class Crtc> + void display<Crtc>::scan_vsync() noexcept + { + if(found_line && crtc.vga_hsync != (col >= hsync_lo)) + { + signal_lost(); + } else if(!found_line) + { + if(crtc.vga_hsync) + { + ++hsync_hi; + } else + { + found_line = true; + } + } else if(vsync_lo == 0) + { + vsync_lo += last_vsync && !crtc.vga_vsync; + } else if(last_hsync && !crtc.vga_hsync) + { + if(crtc.vga_vsync) + { + ++vsync_hi; + } else if(vsync_hi == 0) + { + ++vsync_lo; + } else + { + guess_mode(); + + hsync_lo = hsync_hi = vsync_lo = vsync_hi = 0; + found_line = false; + + scan_syncs(); + } + } + } + + template<class Crtc> + void display<Crtc>::put_pixel() noexcept + { + if(!mode) + { + return; + } + + auto start_h = mode->h.sync + mode->h.back_porch; + auto start_v = mode->v.sync + mode->v.back_porch; + + if(col < start_h + || col >= start_h + mode->h.active + || row < start_v + || row >= start_v + mode->v.active) + { + return; + } + + auto *surface = window ? ::SDL_GetWindowSurface(window) : nullptr; + if(!window || !surface) + { + return; + } + + assert(surface->format->format == SDL_PIXELFORMAT_RGB888); + + auto *pixels = static_cast<std::uint32_t*>(surface->pixels); + auto *pixel = pixels + (row - start_v) * mode->h.active + (col - start_h); + *pixel = (crtc.vga_r & 0xff) << 16 | (crtc.vga_g & 0xff) << 8 | (crtc.vga_b & 0xff); + } + + template<class Crtc> + void display<Crtc>::guess_mode() noexcept + { + auto timings_match = [this](const video_mode &candidate) + { + int dots = candidate.h.active + candidate.h.front_porch + candidate.h.back_porch; + int lines = candidate.v.active + candidate.v.front_porch + candidate.v.back_porch; + + return std::abs(dots - static_cast<int>(hsync_hi)) <= 2 + && std::abs(lines - static_cast<int>(vsync_hi)) <= 2 + && std::abs(static_cast<int>(hsync_lo) - static_cast<int>(candidate.h.sync)) <= 2 + && std::abs(static_cast<int>(vsync_lo) - static_cast<int>(candidate.v.sync)) <= 2; + }; + + if(mode != nullptr && timings_match(*mode)) + { + return; + } + + std::fprintf + ( + stderr, + "[vga] hsync_duty: %u/%u, vsync_duty: %u/%u, pixel_clk: %.2fMHz\n", + hsync_lo, + hsync_lo + hsync_hi, + vsync_lo, + vsync_lo + vsync_hi, + clock_hz / 1e6 + ); + + mode = nullptr; + for(const auto &candidate : MODES) + { + if(!timings_match(candidate)) + { + continue; + } + + float actual_clk_f = clock_hz; + float expected_clk_f = candidate.pixel_clk; + + if(std::fabs((expected_clk_f - actual_clk_f) / expected_clk_f) < 0.02) + { + mode = &candidate; + break; + } + } + + if(mode) + { + auto width = mode->h.active + mode->h.front_porch + mode->h.sync + mode->h.back_porch; + auto height = mode->v.active + mode->v.front_porch + mode->v.sync + mode->v.back_porch; + auto rate = static_cast<float>(clock_hz) / (width * height); + + std::fprintf + ( + stderr, + "[vga] %ux%u @ %.2fHz\n", + mode->h.active, + mode->v.active, + rate + ); + } else + { + std::fputs("[vga] failed to guess mode from timings\n", stderr); + } + + update_window(); + if(mode && window) + { + ::SDL_SetWindowSize(window, mode->h.active, mode->v.active); + } + } + + template<class Crtc> + void display<Crtc>::signal_lost() noexcept + { + if(mode) + { + std::fputs("[vga] no signal\n", stderr); + mode = nullptr; + } + + row = col = 0; + hsync_lo = hsync_hi = vsync_lo = vsync_hi = 0; + found_line = false; + + update_window(); + } + + template<class Crtc> + void display<Crtc>::update_window() noexcept + { + if(!mode && window) + { + ::SDL_DestroyWindow(window); + window = nullptr; + } else if(mode && !window) + { + if(!SDL_WasInit(SDL_INIT_VIDEO)) + { + ::SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1"); + assert(::SDL_Init(SDL_INIT_VIDEO) >= 0); + } + + window = ::SDL_CreateWindow + ( + "VGA Display", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + mode->h.active, + mode->v.active, + 0 + ); + + assert(window); + } + } +} + +#endif diff --git a/tb/vga_domain.sv b/tb/vga_domain.sv new file mode 100644 index 0000000..d3ffbc0 --- /dev/null +++ b/tb/vga_domain.sv @@ -0,0 +1,35 @@ +module vga_domain +( + input logic clk_clk, + reset_reset_n /*verilator public*/ +); + + logic[25:0] avl_address /*verilator public*/; + logic avl_read /*verilator public*/; + logic avl_write /*verilator public*/; + logic[15:0] avl_readdata /*verilator public_flat_rw @(negedge clk_clk)*/; + logic[31:0] avl_writedata /*verilator public*/; + logic avl_waitrequest /*verilator public_flat_rw @(negedge clk_clk)*/; + logic avl_readdatavalid; + logic[3:0] avl_byteenable /*verilator public*/; + + assign avl_write = 0; + assign avl_readdatavalid = avl_read && !avl_waitrequest; + + logic vga_clk /*verilator public*/; + logic vga_hsync /*verilator public*/; + logic vga_vsync /*verilator public*/; + logic vga_blank_n /*verilator public*/; + logic vga_sync_n /*verilator public*/; + logic[7:0] vga_r /*verilator public*/; + logic[7:0] vga_g /*verilator public*/; + logic[7:0] vga_b /*verilator public*/; + + vga crtc + ( + .clk(clk_clk), + .rst_n(reset_reset_n), + .* + ); + +endmodule diff --git a/tb/window.hpp b/tb/window.hpp new file mode 100644 index 0000000..181f3c1 --- /dev/null +++ b/tb/window.hpp @@ -0,0 +1,37 @@ +#ifndef TALLER_wINDOW_HPP +#define TALLER_wINDOW_HPP + +#include <cstdint> +#include <memory> + +#include "avalon.hpp" + +namespace taller::avalon +{ + class window : public slave + { + public: + inline window(slave &downstream, std::uint32_t base) + : slave(base, downstream.address_span(), downstream.word_size()), + downstream(downstream) + {} + + inline virtual bool read(std::uint32_t addr, std::uint32_t &data) final override + { + return downstream.read(addr, data); + } + + inline virtual bool write + ( + std::uint32_t addr, std::uint32_t data, unsigned byte_enable = 0b1111 + ) final override + { + return downstream.write(addr, data, byte_enable); + } + + private: + slave &downstream; + }; +} + +#endif |
