The question code is opening and closing the file every time clk128_tb has an event and is found to equal '1'.
A Minimal, Complete, and Verifiable example with the question's process at it's core:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use std.textio.all;
entity color_out is
end entity;
architecture foo of color_out is
signal clk128_tb: std_logic := '0';
signal color_out_v: std_logic := '0';
type pixel is record
R: unsigned (7 downto 0);
G: unsigned (7 downto 0);
B: unsigned (7 downto 0);
end record;
type scan is array (0 to 3) of pixel;
signal color_out: scan := ( (X"FF", X"A0", X"FF"),
(X"7F", X"7F", X"7F"),
(X"00", X"FF", X"00"),
(X"C0", X"C0", X"C0") );
begin
Verify_data_process :
process
variable my_line: line;
file l_file: TEXT; -- open write_mode is "color_out.txt";
constant header: string := " R G B";
variable file_is_open: boolean;
begin -- process
if not file_is_open then
file_open (l_file, "color_out.txt", write_mode);
file_is_open := true;
-- print(l_file, "R G B" );
write(my_line, header);
writeline(l_file, my_line);
end if;
wait until rising_edge(clk128_tb ); -- wait until clk128_tb = '1';
-- print(l_file, "R G B" );
if color_out_v = '1' then
report "Color_out";
write(my_line, integer'image(to_integer(color_out(0).R)) & " ");
write(my_line, integer'image(to_integer(color_out(0).G)) & " ");
write(my_line, integer'image(to_integer(color_out(0).B)));
writeline(l_file, my_line);
report "write line";
end if;
-- file_close(l_file);
end process;
CLOCK:
process
begin
wait for 10 ns;
clk128_tb <= not clk128_tb;
if now > 100 ns then
wait;
end if;
end process;
STIMULIS:
process
begin
wait for 20 ns;
color_out_v <= '1';
wait;
end process;
end architecture;
The changes include only opening the file once and not closing it explicitly. The file will be closed implicitly when the simulation ends or the process can be passed a signal to close the file explicitly if provided with a sensitivity list. The signal could be provided with a transaction when now = TIME'HIGH for instance.
Also not having a procedure named print, it's been replaced by a constant header string a write to the line buffer and a write line. Note this only occurs once when the file is opened.
The use of the rising_edge function is force of habit. Without an MCVe the code grew organically.
As A. Kieffer commented as an answer integer'image is used plus some added formatting spaces. A line is a access (pointer) to a string, you write strings to a line.
And when run the above gives console output:
ghdl -r color_out
color_out.vhdl:45:13:@30ns:(report note): Color_out
color_out.vhdl:50:13:@30ns:(report note): write line
color_out.vhdl:45:13:@50ns:(report note): Color_out
color_out.vhdl:50:13:@50ns:(report note): write line
color_out.vhdl:45:13:@70ns:(report note): Color_out
color_out.vhdl:50:13:@70ns:(report note): write line
color_out.vhdl:45:13:@90ns:(report note): Color_out
color_out.vhdl:50:13:@90ns:(report note): write line
color_out.vhdl:45:13:@110ns:(report note): Color_out
color_out.vhdl:50:13:@110ns:(report note): write line
And the file color_out.txt contains:
R G B
255 160 255
255 160 255
255 160 255
255 160 255
255 160 255
A set of pixel values for each rising_edge event when color_out_v = '1'. And with the locally static index 0 they're always the same pixel.
So the problem was opening and closing the file repeatedly every time the process is wakened.
A file_open after a file close has the effect of clearing the file contents and you won't have any results.
See IEEE Std 1076-2008 5.5.2 File operations paragraph 2 (excerpted):
— If the value supplied to the Open_Kind parameter is WRITE_MODE, the access mode of the file object is write-only. In addition, the external file is made initially empty. Values written to the file object are placed in the external file in the order in which they are written.