summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2022-12-12 14:34:05 -0600
committerAlejandro Soto <alejandro@34project.org>2022-12-16 16:29:10 -0600
commit02712d69cdd859d702cc7577e72db27d6f0c9ad5 (patch)
tree01d0c36111a99f596588816a2529f661ba56d023
parent397245139af754af07e61a10a91fdc9b9e72153f (diff)
Implement fast video
-rwxr-xr-xsim/sim.py5
-rw-r--r--tb/sim/descifrador.py1
-rw-r--r--tb/top/conspiracion.cpp30
-rw-r--r--tb/vga.hpp16
-rw-r--r--tb/vga.impl.hpp73
5 files changed, 109 insertions, 16 deletions
diff --git a/sim/sim.py b/sim/sim.py
index dceabb4..4a1dfa8 100755
--- a/sim/sim.py
+++ b/sim/sim.py
@@ -342,7 +342,7 @@ do_output = module_get('do_output')
if init := module_get('init'):
init()
-exec_args = [verilated, '--headless', '--dump-regs']
+exec_args = [verilated, '--dump-regs']
cycles = module_get('cycles', 1024)
if cycles is not None:
@@ -351,6 +351,9 @@ if cycles is not None:
if not module_get('enable_tty', False):
exec_args.append('--no-tty')
+if not module_get('enable_video', False):
+ exec_args.append('--headless')
+
for rng in mem_dumps:
length = rng.stop - rng.start
assert rng.start >= 0 and rng.stop > rng.start \
diff --git a/tb/sim/descifrador.py b/tb/sim/descifrador.py
index e754f2a..a77375b 100644
--- a/tb/sim/descifrador.py
+++ b/tb/sim/descifrador.py
@@ -7,6 +7,7 @@ loads = {START: FILE}
consts = {0x30050000: 1, 0x30060000: 0}
cycles = 23000000
mem_dumps = [range(START, START + SIZE)]
+enable_video = True
def final():
words = []
diff --git a/tb/top/conspiracion.cpp b/tb/top/conspiracion.cpp
index f0cfaa2..de43952 100644
--- a/tb/top/conspiracion.cpp
+++ b/tb/top/conspiracion.cpp
@@ -210,6 +210,11 @@ int main(int argc, char **argv)
parser, "headless", "Disable video output", {"headless"}
);
+ args::Flag accurate_video
+ (
+ parser, "accurate-video", "Enable signal-level video emulation", {"accurate-video"}
+ );
+
args::Flag no_tty
(
parser, "no-tty", "Disable TTY takeoveer", {"no-tty"}
@@ -292,16 +297,21 @@ int main(int argc, char **argv)
trace.open("trace.vcd");
}
- interconnect<Vconspiracion_platform> avl(*top.conspiracion->plat);
- interconnect<Vconspiracion_vga_domain> avl_vga(*top.conspiracion->plat->vga);
-
mem<std::uint32_t> hps_ddr3(0x0000'0000, 512 << 20);
jtag_uart ttyJ0(0x3000'0000);
interval_timer timer(0x3002'0000);
mem<std::uint32_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);
+
+ display<Vconspiracion_vga_domain> vga
+ (
+ *top.conspiracion->plat->vga,
+ 0x3800'0000, 25'175'000, 50'000'000
+ );
+
+ interconnect<Vconspiracion_platform> avl(*top.conspiracion->plat);
+ interconnect<Vconspiracion_vga_domain> avl_vga(*top.conspiracion->plat->vga);
std::vector<const_map> consts;
for(const auto &init : *const_)
@@ -309,7 +319,8 @@ int main(int argc, char **argv)
consts.emplace_back(init.addr, init.value);
}
- bool enable_video = !headless;
+ bool enable_fast_video = !headless && !accurate_video;
+ bool enable_accurate_video = !headless && accurate_video;
avl.attach(hps_ddr3);
avl.attach(ttyJ0);
@@ -320,7 +331,10 @@ int main(int argc, char **argv)
avl.attach(slave);
}
- if(enable_video)
+ if(enable_fast_video)
+ {
+ avl.attach(vga);
+ } else if(enable_accurate_video)
{
avl.attach(vram);
avl_vga.attach(vram_window);
@@ -382,14 +396,14 @@ int main(int argc, char **argv)
failed = true;
}
- if(enable_video)
+ if(enable_accurate_video)
{
if(!avl_vga.tick(top.clk_clk))
{
failed = true;
}
- vga.tick(top.clk_clk);
+ vga.signal_tick(top.clk_clk);
}
if(enable_trace)
diff --git a/tb/vga.hpp b/tb/vga.hpp
index 92446ca..578dd17 100644
--- a/tb/vga.hpp
+++ b/tb/vga.hpp
@@ -8,6 +8,8 @@
#include <SDL2/SDL_surface.h>
#include <SDL2/SDL_video.h>
+#include "avalon.hpp"
+
namespace taller::vga
{
struct timings
@@ -26,14 +28,19 @@ namespace taller::vga
};
template<class Crtc>
- class display
+ class display : public avalon::slave
{
public:
- display(Crtc &crtc, std::uint32_t clock_hz) noexcept;
+ display(Crtc &crtc, std::uint32_t base, std::uint32_t clock_hz, std::uint32_t bus_hz = 0) noexcept;
~display() noexcept;
- void tick(bool clk) noexcept;
+ virtual void tick() noexcept final override;
+
+ virtual bool read(std::uint32_t addr, std::uint32_t &data) noexcept final override;
+ virtual bool write(std::uint32_t addr, std::uint32_t data, unsigned byte_enable) noexcept final override;
+
+ void signal_tick(bool clk) noexcept;
inline bool key(std::size_t index)
{
@@ -41,6 +48,9 @@ namespace taller::vga
}
private:
+ unsigned ticks = 0;
+ unsigned refresh_ticks = 0;
+ unsigned max_addr = 0;
Crtc& crtc;
SDL_Window *window = nullptr;
const video_mode *mode = nullptr;
diff --git a/tb/vga.impl.hpp b/tb/vga.impl.hpp
index 9c3294c..988259a 100644
--- a/tb/vga.impl.hpp
+++ b/tb/vga.impl.hpp
@@ -11,6 +11,8 @@
#include <SDL2/SDL_surface.h>
#include <SDL2/SDL_video.h>
+#include "avalon.hpp"
+
namespace
{
// https://web.mit.edu/6.111/www/s2004/NEWKIT/vga.shtml
@@ -35,10 +37,26 @@ namespace
namespace taller::vga
{
template<class Crtc>
- display<Crtc>::display(Crtc &crtc, std::uint32_t clock_hz) noexcept
- : crtc(crtc),
+ display<Crtc>::display(Crtc &crtc, std::uint32_t base, std::uint32_t clock_hz, std::uint32_t bus_hz) noexcept
+ : avalon::slave(base, 64 << 20, 4),
+ crtc(crtc),
clock_hz(clock_hz)
- {}
+ {
+ if(bus_hz > 0)
+ {
+ mode = &MODES[0];
+ max_addr = mode->h.active * mode->v.active;
+
+ refresh_ticks =
+ static_cast<float>(bus_hz)
+ / mode->pixel_clk
+ * (mode->h.active + mode->h.front_porch + mode->h.sync + mode->h.back_porch)
+ * (mode->v.active + mode->v.front_porch + mode->v.sync + mode->v.back_porch);
+
+ ticks = refresh_ticks - 1;
+ update_window();
+ }
+ }
template<class Crtc>
display<Crtc>::~display() noexcept
@@ -48,7 +66,54 @@ namespace taller::vga
}
template<class Crtc>
- void display<Crtc>::tick(bool clk) noexcept
+ void display<Crtc>::tick() noexcept
+ {
+ if(++ticks == refresh_ticks)
+ {
+ ticks = 0;
+ if(!window)
+ {
+ update_window();
+ }
+
+ if(window)
+ {
+ ::SDL_UpdateWindowSurface(window);
+ }
+ }
+ }
+
+ template<class Crtc>
+ bool display<Crtc>::read(std::uint32_t addr, std::uint32_t &data) noexcept
+ {
+ return true;
+ }
+
+ template<class Crtc>
+ bool display<Crtc>::write(std::uint32_t addr, std::uint32_t data, unsigned byte_enable) noexcept
+ {
+ if(!window || !mode)
+ {
+ return true;
+ }
+
+ auto *surface = ::SDL_GetWindowSurface(window);
+ if(!surface)
+ {
+ return true;
+ }
+
+ auto *pixels = static_cast<std::uint32_t*>(surface->pixels);
+ if(addr < max_addr)
+ {
+ pixels[addr] = data;
+ }
+
+ return true;
+ }
+
+ template<class Crtc>
+ void display<Crtc>::signal_tick(bool clk) noexcept
{
if(!clk)
{