00:21
Базовый шаблон C++ SDL2 640x480 — Лисья нора
Оглавление
- Файлы на загрузку
- tb.cc: Main
- tb.h: Основной файл
- video.v: Видеоадаптер
- makefile
- makefile.php
- tb.v: Тестбенч
§ Файлы на загрузку
- Скачать шаблон как ZIP файл
- Верилятор, DE0, MC3, 2Кб текстовый модуль 30 июн 2023
- Верилятор, DE0, 2Кб текстовый модуль 7 янв 2024
§ tb.cc: Main
#include "obj_dir/Vvideo.h" #include "tb.h" // Запуск основного приложения int main(int argc, char** argv) { TB* tb = new TB(argc, argv); return tb->main(); }
§ tb.h: Основной файл
#include <SDL2/SDL.h> #include <stdio.h> class TB { protected: int width, height, scale, frame_length, pticks; int x, y, _hs, _vs; SDL_Surface* screen_surface; SDL_Window* sdl_window; SDL_Renderer* sdl_renderer; SDL_PixelFormat* sdl_pixel_format; SDL_Texture* sdl_screen_texture; SDL_Event evt; Uint32* screen_buffer; // Коррекция тактов int instr; float target; // Модули Vvideo* vga_mod; public: TB(int argc, char** argv) { Verilated::commandArgs(argc, argv); x = 0; y = 0; _hs = 1; _vs = 0; // 100% загрузка процессора instr = 150000; target = 100; pticks = 0; vga_mod = new Vvideo(); // Удвоение пикселей (50 кадров в секунду) scale = 2; width = 640; height = 480; frame_length = (1000/50); if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) { exit(1); } SDL_ClearError(); sdl_window = SDL_CreateWindow("Basic 6.0 Application", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, scale*width, scale*height, SDL_WINDOW_SHOWN); sdl_renderer = SDL_CreateRenderer(sdl_window, -1, SDL_RENDERER_PRESENTVSYNC); screen_buffer = (Uint32*) malloc(width * height * sizeof(Uint32)); sdl_screen_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STREAMING, width, height); SDL_SetTextureBlendMode(sdl_screen_texture, SDL_BLENDMODE_NONE); } // Один кадр int main() { SDL_Rect dstRect; dstRect.x = 0; dstRect.y = 0; dstRect.w = scale * width; dstRect.h = scale * height; int quit = 0; int maxln = frame_length * 25000; do { // Прием событий while (SDL_PollEvent(& evt)) { // Событие выхода switch (evt.type) { case SDL_QUIT: quit = 1; break; } } // Автоматическая коррекция Uint32 ticks = SDL_GetTicks(); for (int i = 0; i < instr; i++) tick(); Uint32 delay = SDL_GetTicks() - ticks; instr = (instr * (0.5 * target) / (float) delay); instr = instr < 1000 ? 1000 : (instr > maxln ? maxln : instr); delay = frame_length - delay; // Дополнение Delay до необходимого количества SDL_Delay(delay < 1 ? 1 : (delay > frame_length ? frame_length : delay)); // Обновить окно SDL_UpdateTexture (sdl_screen_texture, NULL, screen_buffer, width * sizeof(Uint32)); SDL_SetRenderDrawColor (sdl_renderer, 0, 0, 0, 0); SDL_RenderClear (sdl_renderer); SDL_RenderCopy (sdl_renderer, sdl_screen_texture, NULL, & dstRect); SDL_RenderPresent (sdl_renderer); } while (quit == 0); // Удаление окна из памяти free(screen_buffer); SDL_DestroyTexture(sdl_screen_texture); SDL_FreeFormat(sdl_pixel_format); SDL_DestroyRenderer(sdl_renderer); SDL_DestroyWindow(sdl_window); SDL_Quit(); return 0; } // Один такт 25 мгц void tick() { vga_mod->clock = 0; vga_mod->eval(); vga_mod->clock = 1; vga_mod->eval(); vga(vga_mod->hs, vga_mod->vs, (vga_mod->r*16)*65536 + (vga_mod->g*16)*256 + (vga_mod->b*16)); } // ------------------------------UTILS ------------------------------ // Установка точки void pset(int x, int y, Uint32 cl) { if (x >= 0 && y >= 0 && x < width && y < height) screen_buffer[width*y + x] = cl; } // 640 x 480 void vga(int hs, int vs, int color) { if (hs) x++; // Отслеживание изменений HS/VS if (_hs == 0 && hs == 1) { x = 0; y++; } if (_vs == 1 && vs == 0) { x = 0; y = 0; } // Сохранить предыдущее значение _hs = hs; _vs = vs; // Вывод на экран pset(x-48, y-33, color); } };
§ video.v: Видеоадаптер
// verilator lint_off WIDTH module video ( input clock, output reg [3:0] r, output reg [3:0] g, output reg [3:0] b, output hs, output vs ); // --------------------------------------------------------------------- // Тайминги для горизонтальной|вертикальной развертки (640x400) parameter hz_visible = 640, vt_visible = 480, hz_front = 16, vt_front = 10, hz_sync = 96, vt_sync = 2, hz_back = 48, vt_back = 33, hz_whole = 800, vt_whole = 525; // --------------------------------------------------------------------- assign hs = x < (hz_back + hz_visible + hz_front); // NEG. assign vs = y >= (vt_back + vt_visible + vt_front); // POS. // --------------------------------------------------------------------- wire xmax = (x == hz_whole - 1); wire ymax = (y == vt_whole - 1); reg [ 9:0] x = 0; reg [ 9:0] y = 0; // --------------------------------------------------------------------- // Вывод видеосигнала always @(posedge clock) begin {r, g, b} <= 12'h000; // Кадровая развертка x <= xmax ? 0 : x + 1; y <= xmax ? (ymax ? 0 : y + 1) : y; // Вывод окна видеоадаптера if (x >= hz_back && x < hz_visible + hz_back && y >= vt_back && y < vt_visible + vt_back) begin {r, g, b} <= 12'hFFF; end end endmodule
§ makefile
all: php makefile.php run wav: gtkwave tb.gtkw clean: rm -rf tb tb.qqq tb.vcd obj_dir tb.log .diff
§ makefile.php
<?php // Подключение библиотек $LIB = "/usr/share/verilator/include"; $INC = "$LIB/verilated.cpp $LIB/verilated_threads.cpp"; $OBJ = ""; $RUN = isset($argv[1]) && $argv[1] == 'run'; $touchfile = false; $checkfile = []; $files = [ 'tb.v' => 'tb', 'video.v' => 'v', 'tb.cc' => 'c', 'tb.h' => 'c', ]; // Отыкарусить echo `iverilog -g2005-sv -o tb.qqq tb.v video.v`; echo `vvp tb.qqq -o tb.vcd > /dev/null`; if (file_exists('tb.qqq')) { unlink('tb.qqq'); } else { exit; } // ----------------------------------------------------------------------------- // Компиляция Verilator, GCC // ----------------------------------------------------------------------------- if ($RUN) { $run = 1; if (!file_exists(".diff")) mkdir(".diff"); // Скомпилировать, если надо) foreach ($files as $file => $type) { $tf = ".diff/.$file"; $vf = str_replace('.v', '', $file); $ts = file_exists($tf) ? file_get_contents($tf) : 0; $mt = filemtime($file); $e1 = $e2 = false; $checkfile[] = $tf; if ($mt != $ts) { // Добавить в компиляцию if ($type == 'v') { $time = time(); $e1 = (`verilator --threads 1 -cc $file`); $e2 = (`cd obj_dir && make -f V$vf.mk`); echo "[".(time() - $time)."] $file\n"; } $touchfile = true; } if ($type == 'v') { $OBJ .= "obj_dir/V{$vf}__ALL.a "; } file_put_contents($tf, $mt); } // Требуется перекомпиляция if ($touchfile) { $time = time(); $bash = "g++ -Ofast -Wno-unused-result -o tb -I$LIB tb.cc $INC $OBJ -lSDL2 2>&1"; $err = `$bash`; echo "[".(time() - $time)."] GCC\n"; // При обнаруженной ошибке -- сбросить успешные touch-файлы if ($err) { echo "ERROR:\n$bash\n$err"; foreach ($checkfile as $file) unlink($file); $run = 0; } } // Запуск программы -d if ($run) echo `./tb > tb.log`; }
§ tb.v: Тестбенч
`timescale 10ns / 1ns module tb; // --------------------------------------------------------------------- reg clock; reg clock_25; reg reset_n = 1'b0; reg [7:0] memory[1024*1024]; always #0.5 clock = ~clock; always #1.5 clock_25 = ~clock_25; initial begin clock = 0; clock_25 = 0; #3 reset_n = 1; #2000 $finish; end initial begin $dumpfile("tb.vcd"); $dumpvars(0, tb); end // --------------------------------------------------------------------- wire [31:0] address; reg [ 7:0] in; wire [ 7:0] out; wire we; // Контроллер блочной памяти always @(posedge clock) begin in <= memory[address[19:0]]; if (we) memory[address[19:0]] <= out; end // --------------------------------------------------------------------- endmodule