diff --git a/vga/README.md b/vga/README.md index f7fc705..d22d89f 100644 --- a/vga/README.md +++ b/vga/README.md @@ -50,6 +50,12 @@ Then, load the bitstream to the board. The following pattern should be shown in ![VGA demo pattern](vga_demo.png) +### Test + +Sources in [test/hdl/](test/hdl) provide a *Virtual VGA screen* based on the [VGA (RGB image buffer)](https://ghdl.github.io/ghdl-cosim/vhpidirect/examples/arrays.html#vga-rgb-image-buffer) from [ghdl.github.io/ghdl-cosim](https://ghdl.github.io/ghdl-cosim). A VGA monitor (VHDL) reads the output of the UUT and draws frames in a buffer, packing the colours of each pixel in a 32 bit `integer`. After each frame is complete, foreign subprogram `save_screenshot` is executed. Just before finishing the simulation, foreign subprogram `sim_cleanup` is executed. + +[test/hdl/imagemagick/](test/hdl/imagemagick) provides a backend for the virtual screen based on [Imagemagick](https://www.imagemagick.org/). `save_screenshot` saves each frame to a binary file in RGB24 format. Then, `convert` from Imagemagick is used for generating a PNG screenshot. In `sim_cleanup`, `convert` is used for merging all the PNGs into an animated GIF. Images are saved to `test/hdl/imagemagick/out/`. + ## Development Currently, the only supported build system is a [Makefile](Makefile). However, we are willing to support the following managers/runners: diff --git a/vga/src/VGA_config_pkg.vhd b/vga/src/VGA_config_pkg.vhd index d58cdcc..0f8bf99 100644 --- a/vga/src/VGA_config_pkg.vhd +++ b/vga/src/VGA_config_pkg.vhd @@ -1,6 +1,6 @@ package VGA_config is -type VGA_config is record +type VGA_config_t is record width : natural; -- W: width height : natural; -- H: height rate : natural; -- R: refresh rate @@ -19,7 +19,7 @@ end record; -- __ __ -- _| |XXXXXXX________XXXX| -type VGA_configs_t is array (natural range <>) of VGA_config; +type VGA_configs_t is array (natural range <>) of VGA_config_t; constant VGA_configs : VGA_configs_t(0 to 61) := ( -- W H R C hS hF hB vS vF vB hP vP 0 => ( 640, 350, 70, 25175, 96, 48, 16, 2, 60, 37, false, true ), diff --git a/vga/src/VGA_sync_gen_cfg.vhd b/vga/src/VGA_sync_gen_cfg.vhd index 0ed2aa0..90f4af1 100644 --- a/vga/src/VGA_sync_gen_cfg.vhd +++ b/vga/src/VGA_sync_gen_cfg.vhd @@ -1,11 +1,11 @@ library ieee; context ieee.ieee_std_context; -use work.VGA_config.all; +use work.VGA_config.VGA_config_t; entity VGA_sync_gen_cfg is generic ( - CONFIG: VGA_config + CONFIG: VGA_config_t ); port ( CLK: in std_logic; diff --git a/vga/src/VGA_sync_gen_idx.vhd b/vga/src/VGA_sync_gen_idx.vhd index 507adbc..d46e951 100644 --- a/vga/src/VGA_sync_gen_idx.vhd +++ b/vga/src/VGA_sync_gen_idx.vhd @@ -35,7 +35,7 @@ begin begin if rising_edge(CLK) then if RST then - st <= 0; + st <= 2; else if EN and p_tc then st <= 0 when st = 3 else st + 1; diff --git a/vga/src/demo.vhd b/vga/src/demo.vhd index 93f8008..cc5c7fb 100644 --- a/vga/src/demo.vhd +++ b/vga/src/demo.vhd @@ -5,7 +5,7 @@ use work.VGA_config.all; architecture demo of Design_Top is - constant cfg : VGA_config := VGA_configs(G_SCREEN); + constant cfg : VGA_config_t := VGA_configs(G_SCREEN); constant ymid : integer range -1 to cfg.height := cfg.height/2; constant xmid : integer range -1 to cfg.width := cfg.width/2; @@ -30,9 +30,7 @@ architecture demo of Design_Top is begin i_sync: entity work.VGA_sync_gen_cfg - generic map ( - CONFIG => cfg - ) + generic map ( cfg ) port map ( CLK => CLK, EN => EN, diff --git a/vga/test/.gitignore b/vga/test/.gitignore new file mode 100644 index 0000000..4f81b70 --- /dev/null +++ b/vga/test/.gitignore @@ -0,0 +1,2 @@ +!/imagemagick/out/.gitkeep +/imagemagick/out/ \ No newline at end of file diff --git a/vga/test/hdl/screen.vhd b/vga/test/hdl/VGA_screen.vhd similarity index 71% rename from vga/test/hdl/screen.vhd rename to vga/test/hdl/VGA_screen.vhd index 0092b71..a8cb651 100644 --- a/vga/test/hdl/screen.vhd +++ b/vga/test/hdl/VGA_screen.vhd @@ -1,9 +1,9 @@ library ieee; context ieee.ieee_std_context; -use work.vga_cfg_pkg.all; +use work.VGA_config.all; -entity vga_screen is +entity VGA_screen is generic ( G_SCREEN : natural; G_WWIDTH : natural := 0; -- X11 window width @@ -16,8 +16,8 @@ entity vga_screen is RGB: in std_logic_vector(2 downto 0); VID: in std_logic ); - constant cfg: VGA_config := VGA_configs(G_SCREEN); - package tb_pkg is new work.pkg + constant cfg: VGA_config_t := VGA_configs(G_SCREEN); + package tb_pkg is new work.VGA_screen_pkg generic map ( G_WIDTH => cfg.width, G_HEIGHT => cfg.height @@ -25,7 +25,7 @@ entity vga_screen is use tb_pkg.all; end entity; -architecture arch of vga_screen is +architecture arch of VGA_screen is constant clk_period : time := (1.0/real(cfg.clk)) * 1 ms; @@ -36,36 +36,10 @@ architecture arch of vga_screen is begin - proc_init: process - begin - -- X11 window size - if G_WWIDTH /= 0 and G_WHEIGHT /= 0 then - sim_init(G_WWIDTH, G_WHEIGHT); - else - sim_init(cfg.width, cfg.height); - end if; - wait; - end process; - - proc_clk: process - begin - clk <= '0'; wait for clk_period/2; - clk <= '1'; wait for clk_period/2; - end process; + clk <= not clk after clk_period/2; - i_sync: entity work.vga_sync_gen - generic map ( - G_HPULSE => cfg.hpulse, - G_HFRONT => cfg.hfront, - G_WIDTH => cfg.width, - G_HBACK => cfg.hback, - G_HPOL => cfg.hpol, - G_VPULSE => cfg.vpulse, - G_VFRONT => cfg.vfront, - G_HEIGHT => cfg.height, - G_VBACK => cfg.vback, - G_VPOL => cfg.vpol - ) + i_sync: entity work.VGA_sync_gen_cfg + generic map ( cfg ) port map ( CLK => clk, EN => '1', @@ -115,6 +89,7 @@ begin if rising_edge(clk) then sync(1) <= sync(0); if sync="01" then + report "save screenshot " & to_string(frame) severity note; save_screenshot( screen, screen'length(2), --width diff --git a/vga/test/hdl/pkg.vhd b/vga/test/hdl/VGA_screen_pkg.vhd similarity index 82% rename from vga/test/hdl/pkg.vhd rename to vga/test/hdl/VGA_screen_pkg.vhd index 226e5a9..6c53963 100644 --- a/vga/test/hdl/pkg.vhd +++ b/vga/test/hdl/VGA_screen_pkg.vhd @@ -1,7 +1,7 @@ library ieee; context ieee.ieee_std_context; -package pkg is +package VGA_screen_pkg is generic ( G_WIDTH : natural := 640; G_HEIGHT : natural := 480 @@ -13,9 +13,6 @@ package pkg is shared variable screen: screen_t; - procedure sim_init (width, height: natural); - attribute foreign of sim_init : procedure is "VHPIDIRECT sim_init"; - -- TODO: pass a fat pointer, so that width and height need not to be passed explicitly procedure save_screenshot ( variable ptr: screen_t; @@ -31,13 +28,9 @@ package pkg is function RGB_to_integer (rgb: std_logic_vector(2 downto 0)) return integer; -end pkg; - -package body pkg is +end VGA_screen_pkg; - procedure sim_init (width, height: natural) is - begin report "VHPIDIRECT sim_init" severity failure; - end procedure; +package body VGA_screen_pkg is procedure save_screenshot ( variable ptr: screen_t; @@ -65,4 +58,4 @@ package body pkg is return to_integer(unsigned(raw24)); end function; -end pkg; +end VGA_screen_pkg; diff --git a/vga/test/hdl/tb.vhd b/vga/test/hdl/VGA_tb.vhd similarity index 79% rename from vga/test/hdl/tb.vhd rename to vga/test/hdl/VGA_tb.vhd index 70761e5..303fcc2 100644 --- a/vga/test/hdl/tb.vhd +++ b/vga/test/hdl/VGA_tb.vhd @@ -4,15 +4,17 @@ use std.env.stop; library ieee; context ieee.ieee_std_context; +use work.VGA_config.all; + entity tb_vga is generic ( - G_SCREEN : natural := 4 + G_SCREEN : natural := 22 ); end entity; architecture arch of tb_vga is - constant clk_period : time := 20 ns; + constant clk_period : time := (1.0/real(VGA_configs(G_SCREEN).clk)) * 1 ms; signal clk, rst, save_video: std_logic := '0'; type vga_t is record @@ -25,11 +27,7 @@ architecture arch of tb_vga is begin - proc_clk: process - begin - clk <= '0'; wait for clk_period/2; - clk <= '1'; wait for clk_period/2; - end process; + clk <= not clk after clk_period/2; proc_main: process begin @@ -37,7 +35,7 @@ begin rst <= '1'; wait for 10*clk_period; rst <= '0'; - wait for 35 ms; + wait for 100 ms; report "end simulation" severity note; save_video <= '1'; wait for 200 ns; @@ -45,8 +43,7 @@ begin wait; end process; - - VIRT_VGA: entity work.vga_screen + VIRT_VGA: entity work.VGA_screen generic map ( G_SCREEN => G_SCREEN ) @@ -58,8 +55,7 @@ begin VID => save_video ); - - UUT: entity work.vga_pattern + UUT: entity work.Design_Top(demo) generic map ( G_SCREEN => G_SCREEN ) diff --git a/vga/test/hdl/vga_clk.vhd b/vga/test/hdl/vga_clk.vhd deleted file mode 100644 index 812eb9e..0000000 --- a/vga/test/hdl/vga_clk.vhd +++ /dev/null @@ -1,27 +0,0 @@ -library ieee; -context ieee.ieee_std_context; - -entity vga_clk is - generic ( - G_PERIOD: time - ); - port ( - RST: in std_logic; - CIN: in std_logic; - EIN: in std_logic; - COUT: out std_logic; - EOUT: out std_logic - ); -end entity; - -architecture arch of vga_clk is -begin - proc_clk: process - begin - COUT <= '0'; - wait for G_PERIOD/2; - COUT <= '1'; - wait for G_PERIOD/2; - end process; - EOUT <= EIN; -end architecture; \ No newline at end of file diff --git a/vga/test/caux.c b/vga/test/imagemagick/caux.c similarity index 89% rename from vga/test/caux.c rename to vga/test/imagemagick/caux.c index 89159a3..5d6cbd9 100644 --- a/vga/test/caux.c +++ b/vga/test/imagemagick/caux.c @@ -4,10 +4,6 @@ #include #include -void sim_init(uint32_t width, uint32_t height) { - printf("sim_init is not required, thus empty, in this implementation\n"); -} - void integer_to_raw24(int32_t *ptr, uint32_t width, uint32_t height, uint8_t *raw24) { int x, y; uint32_t *q = ptr; @@ -23,6 +19,10 @@ void integer_to_raw24(int32_t *ptr, uint32_t width, uint32_t height, uint8_t *ra } void save_screenshot(int32_t *ptr, uint32_t width, uint32_t height, int id) { + if ( id == 0 ) { + return; + } + assert(ptr != NULL); uint8_t *raw24 = malloc(width * height * 3); diff --git a/vga/test/out/.gitkeep b/vga/test/imagemagick/out/.gitkeep similarity index 100% rename from vga/test/out/.gitkeep rename to vga/test/imagemagick/out/.gitkeep diff --git a/vga/test/run.sh b/vga/test/imagemagick/run.sh similarity index 69% rename from vga/test/run.sh rename to vga/test/imagemagick/run.sh index 445ce04..b097743 100644 --- a/vga/test/run.sh +++ b/vga/test/imagemagick/run.sh @@ -5,10 +5,12 @@ set -e cd $(dirname "$0") echo "> Analyze ../src/*.vhd and ./hdl/*.vhd" -ghdl -i --std=08 -frelaxed ../src/*.vhd ./hdl/*.vhd +ghdl -i --std=08 -frelaxed ../../src/*.vhd ../hdl/*.vhd echo "> Build tb (with caux.c)" ghdl -m --std=08 -frelaxed -Wl,caux.c -o tb tb_vga +rm *.o *.cf + echo "> Execute tb (save wave.ghw)" -./tb --wave=wave.ghw +./tb --wave=out/wave.ghw