snake-vhdl/simulation/VGASimulator.py
2021-12-28 18:45:25 +01:00

197 lines
5.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
'''VGASimulator.py - Pedro José Pereira Vieito © 2016
View VGA output from a VHDL simulation.
Ported from VGA Simulator:
https://github.com/MadLittleMods/vga-simulator
by Eric Eastwood <contact@ericeastwood.com>
More info about how to generate VGA output from VHDL simulation here:
http://ericeastwood.com/blog/8/vga-simulator-getting-started
Usage:
VGASimulator.py <file> [<frames>]
Options:
-h, --help Show this help
'''
import sys
import os
import re
import struct
from PIL import Image
from docopt import docopt
__author__ = "Pedro José Pereira Vieito"
__email__ = "pvieito@gmail.com"
def time_conversion(unit_from, unit_to, value):
# convert between the following:
# fs, ps, ns, us, ms, sec, min, hr
unit_dict = {
"fs": .000000000000001,
"ps": .000000000001,
"ns": .000000001,
"us": .000001,
"ms": .001,
"s": 1,
"sec": 1,
"min": 60,
"hr": 3600,
}
return unit_dict[unit_from] / unit_dict[unit_to] * value
def bin_to_color(binary):
# Returns a value 0-255 corresponding to the bit depth
# of the binary number and the value.
# This is why your rgb values need to be padded to the full bit depth
return int(int(binary, 2) / int("1" * len(binary), 2) * 255)
def render_vga(file):
vga_file = open(file, 'r')
# From: http://tinyvga.com/vga-timing/
res_x = 640
res_y = 480
# Pixel Clock: ~10 ns, 108 MHz
pixel_clk = 40e-9
back_porch_x = 48 #318
back_porch_y = 33 #38
h_counter = 0
v_counter = 0
back_porch_x_count = 0
back_porch_y_count = 0
last_hsync = -1
last_vsync = -1
time_last_line = 0 # Time from the last line
time_last_pixel = 0 # Time since we added a pixel to the canvas
frame_count = 0
vga_output = None
print('[ ] VGA Simulator')
print('[ ] Resolution:', res_x, '×', res_y)
for vga_line in vga_file:
pattern = re.compile("^([0-9]+) (fs|ps|ns|us|ms|sec|min|hr): "
"(0|1) (0|1) ((?:0|1)+) ((?:0|1)+) "
"((?:0|1)+)")
match = pattern.match(vga_line)
if (match):
time = time_conversion(match.group(2), "sec", int(match.group(1)))
hsync = int(match.group(3))
vsync = int(match.group(4))
red = bin_to_color(match.group(5))
green = bin_to_color(match.group(6))
blue = bin_to_color(match.group(7))
time_last_pixel += time - time_last_line
if last_hsync == 0 and hsync == 1:
h_counter = 0
# Move to the next row, if past back porch
if back_porch_y_count >= back_porch_y:
v_counter += 1
# Increment this so we know how far we are
# after the vsync pulse
back_porch_y_count += 1
# Set this to zero so we can count up to the actual
back_porch_x_count = 0
# Sync on sync pulse
time_last_pixel = 0
if last_vsync == 0 and vsync == 1:
# Show frame or create new frame
if vga_output:
print("Showing frame")
vga_output.show("VGA Output")
else:
print("creating frame")
vga_output = Image.new('RGB', (res_x, res_y), (0, 0, 0))
if frame_count < frames_limit or frames_limit == -1:
print("[+] VSYNC: Decoding frame", frame_count)
frame_count += 1
h_counter = 0
v_counter = 0
# Set this to zero so we can count up to the actual
back_porch_y_count = 0
# Sync on sync pulse
time_last_pixel = 0
else:
print("[ ]", frames_limit, "frames decoded")
exit(0)
if vga_output and vsync:
# Add a tolerance so that the timing doesn't have to be bang on
tolerance = 5e-9
if time_last_pixel >= (pixel_clk - tolerance) and \
time_last_pixel <= (pixel_clk + tolerance):
# Increment this so we know how far we are
# After the hsync pulse
back_porch_x_count += 1
# If we are past the back porch
# Then we can start drawing on the canvas
if back_porch_x_count >= back_porch_x and \
back_porch_y_count >= back_porch_y:
# Add pixel
if h_counter < res_x and v_counter < res_y:
vga_output.putpixel((h_counter, v_counter),
(red, green, blue))
# Move to the next pixel, if past back porch
if back_porch_x_count >= back_porch_x:
h_counter += 1
# Reset time since we dealt with it
time_last_pixel = 0
last_hsync = hsync
last_vsync = vsync
time_last_line = time
vga_output.show("VGA Output")
vga_output.save("out.png")
args = docopt(__doc__)
file = args['<file>']
if args['<frames>']:
frames_limit = int(args['<frames>'])
else:
frames_limit = -1
vga_extensions = ['.vga', '.txt']
if os.path.isfile(file) and os.path.splitext(file)[1] in vga_extensions:
render_vga(file)
print("[ ]", "Final frame decoded")
else:
print('[x] VGA output file (.vga) not found.')