Оглавление
§ Файлы на загрузку
§ 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;
instr = 150000;
target = 100;
pticks = 0;
vga_mod = new Vvideo();
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;
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;
}
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));
}
void pset(int x, int y, Uint32 cl)
{
if (x >= 0 && y >= 0 && x < width && y < height)
screen_buffer[width*y + x] = cl;
}
void vga(int hs, int vs, int color)
{
if (hs) x++;
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: Видеоадаптер
module video
(
input clock,
output reg [3:0] r,
output reg [3:0] g,
output reg [3:0] b,
output hs,
output vs
);
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);
assign vs = y >= (vt_back + vt_visible + vt_front);
wire xmax = (x == hz_whole - 1);
wire ymax = (y == vt_whole - 1);
reg [10:0] x = 0;
reg [10: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;
}
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";
if ($err) { echo "ERROR:\n$bash\n$err"; foreach ($checkfile as $file) unlink($file); $run = 0; }
}
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