-- probedisplay.vhd -- Nov. 23, 2000 -- written by Graeme Fricke -- partially derived from the logic analyzer and Arkanoid projects of previous EE552 -- classes. -- Enthusiastic thanks are given to John Koob, the class TA, for timing-related -- debugging assistance which went above and beyond the call. library IEEE; use IEEE.STD_LOGIC_1164.all; use IEEE.STD_LOGIC_ARITH.all; use IEEE.STD_LOGIC_UNSIGNED.all; LIBRARY lpm; USE lpm.lpm_components.ALL; -- the probe display black box entity probedisplay is PORT( clock : in STD_LOGIC; vga_red, vga_green, vga_blue : out BOOLEAN; HSync, VSync : out STD_LOGIC; volt_value1, volt_value2, volt_value3, volt_value4 : in STD_LOGIC_VECTOR(3 downto 0); volt_units, volt_range : in STD_LOGIC_VECTOR(1 downto 0); axis_enabled : in STD_LOGIC_VECTOR(2 downto 0); over_range: in STD_LOGIC; zeroed: in STD_LOGIC; bat_value1, bat_value2, bat_value3, bat_value4 : in STD_LOGIC_VECTOR(3 downto 0); bat_status : in STD_LOGIC_VECTOR(1 downto 0); -- temp_value1, temp_value2 : in STD_LOGIC_VECTOR(3 downto 0); reset: in STD_LOGIC; noise: buffer STD_LOGIC); END probedisplay; architecture organization of probedisplay is -- video display signals and constants signal countX,countY: STD_LOGIC_VECTOR(9 downto 0); signal rom_address: STD_LOGIC_VECTOR(8 Downto 0); signal rom_data: STD_LOGIC_VECTOR(7 Downto 0); signal col_address: STD_LOGIC_VECTOR(6 Downto 0); signal row_address: STD_LOGIC_VECTOR(5 Downto 0); signal rom_mux_output: STD_LOGIC; signal format_address: STD_LOGIC_VECTOR(7 downto 0); signal format_data: STD_LOGIC_VECTOR(5 downto 0); signal red, green, blue: boolean; signal in_screen, in_screen_vertical, in_screen_horizontal: BOOLEAN; signal temp_rom: STD_LOGIC_VECTOR(7 downto 0); signal test_count: STD_LOGIC_VECTOR(9 downto 0); signal HSync1, HSync2, VSync1, VSync2: STD_LOGIC; signal barsize_bit: STD_LOGIC_VECTOR(9 downto 0); constant countXmax: STD_LOGIC_VECTOR(9 downto 0) := "1100011111"; constant countYmax: STD_LOGIC_VECTOR(9 downto 0) := "1000001100"; begin --Small 8 by 8 Character Generator ROM for Video Display reg_character: lpm_rom-- loading the character set into rom GENERIC MAP ( lpm_widthad => 9, lpm_numwords => 384, lpm_outdata => "UNREGISTERED", lpm_address_control => "UNREGISTERED", -- Reads in mif file for character generator data lpm_file => "chars.mif", lpm_width => 8) PORT MAP ( address => rom_address, q => rom_data); -- ROM for determining character layout on screen format_rom: lpm_rom GENERIC MAP ( lpm_widthad => 8, lpm_numwords => 185, lpm_outdata => "UNREGISTERED", lpm_address_control => "UNREGISTERED", -- Reads in mif file for character display format lpm_file => "screenwords.mif", lpm_width => 6) PORT MAP ( address => format_address, q => format_data); -- Process to line up the monitor synchronization output signals and the monitor -- colour output signals. Generates horizontal and vertical counters which count through -- all pixels, horizontally, row by row, taking into account the extra time required for -- motion of the monitor gun. Includes some fiddling to ensure that signals occur at the -- right time (if theory matched reality this wouldn't be necessary). synchronize: process(clock) begin if rising_edge(clock) then if (countX >= countXmax) then countX <= (others => '0'); else countX <= countX + '1'; end if; if (countX <= 755) and (countX >= 659) then HSync2 <= '0'; else HSync2 <= '1'; end if; if (countY >= countYmax) and (countX >=699) then countY <= (others => '0'); else if (countX =699) then countY <= countY + '1'; end if; end if; if (countY <= 494) and (countY >= 493) then VSync2 <= '0'; else VSync2 <= '1'; end if; if (countX <= 639) then in_screen_horizontal <= true; else in_screen_horizontal <= false; end if; if (countY <= 479) then in_screen_vertical <= true; else in_screen_vertical <= false; end if; -- The column count needs to be altered in order to prevent some pixel values from being repeated -- in the character after the character in which they're supposed to appear. if (countX < 6) then col_address <= countX(9 downto 3); else test_count <= countX + 2; col_address <= test_count(9 downto 3); end if; -- This is a bit kludgy, but it means that row_address can be defined in 6 bits rather than 7. -- There's no other obvious effect that I can see. if (countY >= 480) then row_address <= "111100"; else row_address <= countY(8 downto 3); end if; -- This has the effect of holding back the synchronization signals by 2 clock cycles, which -- helps line things up. HSync1<= HSync2; HSync <= HSync1; VSync1<= VSync2; VSync <= VSync1; end if; end process synchronize; outsignals: process(clock) begin if rising_edge(clock) then rom_address(2 Downto 0) <= countY(2 downto 0); -- Mux to pick off correct rom data bit from 8-bit word -- This is needed because while the column count goes from left to right on screen, -- the same pixels are indexed from right to left in rom_data. rom_mux_output <= rom_data ( CONV_INTEGER(NOT countX(2 downto 0))); -- Sets the colour of each pixel on-screen, row by row. If a pixel is in use, -- rom_mux_output = 1, so (rom_mux_output = '0') is false and the associated colour -- signal is false or off. If the pixel isn't in use, the colour defaults to a white -- background. -- Note that this means for cases where the pixel is supposed to be black, no further -- condition is required - the inverse of white is black. This eliminates the need -- for specifying colour settings for black characters and lines. case row_address is when "000101" => -- display reading value and units if col_address >= "0010010" and col_address <= "0011101" then red <= (rom_mux_output = '0'); green <= (rom_mux_output = '0'); blue <= true; --display UNIT ZEROED if zeroed elsif col_address >= "0110111" and col_address <= "1000001" and zeroed = '1' then red <= (rom_mux_output = '0'); green <= true; blue <= (rom_mux_output = '0'); -- display UNIT NOT ZEROED if not zeroed elsif col_address >= "0110111" and col_address <= "1000101" and zeroed = '0' then red <= true; green <= (rom_mux_output = '0'); blue <= (rom_mux_output = '0'); else red <= (rom_mux_output = '0'); green <= (rom_mux_output = '0'); blue <= (rom_mux_output = '0'); end if; when "001101" => -- display OVER RANGE! if over range -- if col_address >= "0001101" and col_address <= "0010111" and over_range = '1' then -- red <= true; -- green <= (rom_mux_output = '0'); -- blue <= (rom_mux_output = '0'); -- display temperature value and units -- elsif col_address >= "1000100" and col_address <= "1001000" then -- red <= (rom_mux_output = '0'); -- green <= (rom_mux_output = '0'); -- blue <= true; -- else -- display the whole row in red; both are conditional displays red <= true; green <= (rom_mux_output = '0'); blue <= (rom_mux_output = '0'); -- end if; when "010111" => -- display battery voltage and unit if col_address >= "1000000" and col_address <= "1000110" then red <= (rom_mux_output = '0'); green <= (rom_mux_output = '0'); blue <= true; else red <= (rom_mux_output = '0'); green <= (rom_mux_output = '0'); blue <= (rom_mux_output = '0'); end if; when "001001" => -- display range values if col_address >= "0010010" and col_address <= "0011010" then red <= (rom_mux_output = '0'); green <= (rom_mux_output = '0'); blue <= true; -- display enabled axes elsif col_address >= "0111101" and col_address <= "1000001" then red <= (rom_mux_output = '0'); green <= (rom_mux_output = '0'); blue <= true; else red <= (rom_mux_output = '0'); green <= (rom_mux_output = '0'); blue <= (rom_mux_output = '0'); end if; when "011011" => -- display battery status -- choose between level indicators: OK, LOW, FAIL if col_address >= "0111110" and col_address <= "1000001" then case bat_status is when "00" => red <= (rom_mux_output = '0'); green <= true; blue <= (rom_mux_output = '0'); when others => red <= true; green <= (rom_mux_output = '0'); blue <= (rom_mux_output = '0'); end case; else red <= (rom_mux_output = '0'); green <= (rom_mux_output = '0'); blue <= (rom_mux_output = '0'); end if; when "100100" => --display y axis if col_address = "0001001" then red <= false; green <= false; blue <= false; else red <= true; blue <= true; green <= true; end if; when "100101" => --display y axis if col_address = "0001001" then red <= false; green <= false; blue <= false; else red <= true; blue <= true; green <= true; end if; when "100110" => --display y axis if col_address = "0001001" then red <= false; green <= false; blue <= false; -- display bar elsif test_count >="0001010000" and test_count <= barsize_bit then red <= false; blue <= true; green <= false; else red <= true; blue <= true; green <= true; end if; when "100111" => -- display y axis if col_address = "0001001" then red <= false; green <= false; blue <= false; -- display bar elsif test_count >="0001010001" and test_count <= barsize_bit then red <= false; blue <= true; green <= false; else red <= true; blue <= true; green <= true; end if; when "101000" => --display y axis if col_address = "0001001" then red <= false; green <= false; blue <= false; else red <= true; blue <= true; green <= true; end if; when "101001" => -- display x axis if col_address >= "0001001" and col_address <= "0110010" then red <= false; blue <= false; green <= false; else red <= true; blue <= true; green <= true; end if; -- all other pixels are either black or white when others => red <= (rom_mux_output = '0'); green <= (rom_mux_output = '0'); blue <= (rom_mux_output = '0'); end case; end if; end process outsignals; -- This process passes the colour signals to the outside world. colors: process(clock) begin if reset = '0' then vga_Red <= false; vga_Green <= false; vga_Blue <= false; in_screen <= false; elsif rising_edge(clock) then -- determine the display area in_screen <= in_screen_horizontal and in_screen_vertical; -- The RGB signal pins to the VGA monitor; colour isn't defined -- when we're outside the screen area. vga_Red <= red and in_screen; vga_Green <= green and in_screen; vga_Blue <= blue and in_screen; end if; end process colors; -- Defines the length of the bar graph based on the probe value -- reading. This process adds a significant chunk of logic cells -- to the title thanks to the multipliers. varybar: process variable barsize_int: integer:= 0; begin case volt_range is when "00" | "01" => barsize_int := (10*CONV_INTEGER(volt_value1 - "0011")) + CONV_INTEGER(volt_value2 - "0011"); when "10" | "11" => barsize_int := (100*CONV_INTEGER(volt_value1 - "0011")) + (10*CONV_INTEGER(volt_value2 - "0011")) + CONV_INTEGER(volt_value3 - "0011"); when others => barsize_int := 0; end case; barsize_bit <= '0' & ("001010000" + CONV_STD_LOGIC_VECTOR(barsize_int, 9)); -- if the reading value is high then turn the siren on if barsize_int > 20 then noise <= '1'; else noise <= '0'; end if; end process varybar; -- Defines whether each pixel is supposed to be on or off. Based on 80x60 -- character array (divides the 640x480 pixel screen into 8x8 pixel characters -- (with the exception of the bar graph, which is defined on a pixel-by-pixel -- basis within a row). Goes through the screen row by row. Based on column -- position within each row, asks the format ROM for the character which should be -- displayed at that position (or requests a blank if there's no character needed). -- Then asks the character ROM for the status of each pixel within the character. -- Shortcuts are taken when linking column values to matching format ROM -- addresses; the shorter the adders involved in the calculation, the better, both -- for timing and for logic cell requirements and it's rarely necessary to base the -- linking calculation on all of the bits in the column value. display_character: process begin case row_address is when "000101" => -- display VALUE: if col_address >= "0001011" and col_address <= "0010000" then format_address <= "000" & (col_address(4 downto 0) + "00010"); -- display reading value elsif col_address = "0010010" then format_address <= "0101" & (volt_value1 + "0011"); elsif col_address = "0010011" then format_address <= "0101" & (volt_value2 + "0011"); elsif col_address = "0010100" then case volt_range is when "00" | "01" => format_address <= "01100000"; when "10" | "11" => format_address <= "0101" & (volt_value3 + "0011"); when others => format_address <= "01111111"; end case; elsif col_address = "0010101" then case volt_range is when "00" | "01" => format_address <= "0101" & (volt_value3 + "0011"); when "10" | "11" => format_address <= "01100000"; when others => format_address <= "01111111"; end case; elsif col_address = "0010110" then format_address <= "0101" & (volt_value4 + "0011"); -- display reading units elsif col_address >= "0011000" and col_address <= "0011101" then case volt_units is when "00" => format_address <= "0110" & (col_address(3 downto 0) + "1111"); when "01" => format_address <= "011" & (col_address(4 downto 0) + "10101"); when "10" => format_address <= "01111" & (col_address(2 downto 0) + "001"); when others => format_address <= "01111111"; end case; -- display UNIT ZEROED if zeroed elsif col_address >= "0110111" and col_address <= "1000001" and zeroed = '1' then format_address <= "0010" & (col_address(3 downto 0) - "0011"); -- display UNIT NOT ZEROED if not zeroed elsif col_address >= "0110111" and col_address <= "1000101" and zeroed = '0' then format_address <= "101" & (col_address(4 downto 0) - "01101"); else format_address <= "01111111"; end if; when "000001" => -- display HI-4422 PROBE if col_address >= "0100101" and col_address <= "0110001" then format_address <= "00" & (col_address(5 downto 0) - "100101"); else format_address <= "01111111"; end if; when "001001" => -- display RANGE: if col_address >= "0001011" and col_address <= "0010000" then format_address <= "0001" & (col_address(3 downto 0) + "1000"); -- display range values elsif col_address >= "0010010" and col_address <= "0011010" then case volt_range is when "00" => format_address <= "100" & (col_address(4 downto 0) - "10010"); when "01" => format_address <= "100" & (col_address(4 downto 0) - "01001"); when "10" => format_address <= '1' & col_address; when "11" => format_address <= "10" & (col_address(5 downto 0) + "001001"); when others => format_address <= "01111111"; end case; -- display AXES: elsif col_address >= "0110111" and col_address <= "0111011" then format_address <= '0' & (col_address + "0000110"); -- display enabled axes elsif col_address = "0111101" and axis_enabled(2) = '1' then format_address <= "01100001"; elsif col_address = "0111111" and axis_enabled(1) = '1' then format_address <= "01100010"; elsif col_address = "1000001" and axis_enabled(0) = '1' then format_address <= "01100011"; else format_address <= "01111111"; end if; when "001101" => -- display OVER RANGE! if over range if col_address >= "0001101" and col_address <= "0010111" and over_range = '1' then format_address <= "00" & (col_address(5 downto 0) + "001100"); -- display TEMPERATURE: -- elsif col_address >= "0110111" and col_address <= "1000010" then -- format_address <= "0100" & (col_address(3 downto 0) + "1011"); -- display temperature value -- elsif col_address = "1000100" then -- format_address <= "0101" & (temp_value1 + "0011"); -- elsif col_address = "1000101" then -- format_address <= "0101" & (temp_value2 + "0011"); -- display degrees C -- elsif col_address >= "1000111" and col_address <= "1001000" then -- format_address <= "011001" & (col_address(1 downto 0) + "01"); -- display WARNING! if voltage reading is high elsif col_address >= "0110111" and col_address <= "0111110" and noise = '1' then format_address <= "0100" & (col_address(3 downto 0) + "1011"); else format_address <= "01111111"; end if; when "010111" => -- display BATTERY: if col_address >= "0110111" and col_address <= "0111110" then format_address <= "010" & (col_address(4 downto 0) + "10111"); -- display battery voltage and unit (V) elsif col_address = "1000000" then format_address <= "0101" & (bat_value1 + "0011"); elsif col_address = "1000001" then format_address <= "0101" & (bat_value2 + "0011"); elsif col_address = "1000010" then format_address <= "01100000"; elsif col_address = "1000011" then format_address <= "0101" & (bat_value3 + "0011"); elsif col_address = "1000100" then format_address <= "0101" & (bat_value4 + "0011"); elsif col_address = "1000110" then format_address <= "01100110"; else format_address <= "01111111"; end if; when "011011" => -- display battery status -- display LEVEL: if col_address >= "0110111" and col_address <= "0111100" then format_address <= "1010" & (col_address(3 downto 0) - "0011"); -- choose between level indicators: OK, LOW, FAIL elsif col_address >= "0111110" and col_address <= "1000001" then case bat_status is when "00" => format_address <= "0011" & (col_address(3 downto 0) - "0111"); when "01" => format_address <= "00110" & (col_address(2 downto 0) - "011"); when "10" => format_address <= "001" & (col_address(4 downto 0) - "01111"); when others => format_address <= "01111111"; end case; else format_address <= "01111111"; end if; when "100001" => -- display VALUE above graph if col_address >= "0011001" and col_address <= "0011101" then format_address <= "000" & (col_address(4 downto 0) - "01100"); else format_address <= "01111111"; end if; when "101011" => -- display x axis levels case col_address is -- 0 when "0001001" => format_address <= "01010110"; -- 100 when "0010101" => format_address <= "01010111"; when "0010110" => format_address <= "01010110"; when "0010111" => format_address <= "01010110"; -- 200 when "0100010" => format_address <= "01011000"; when "0100011" => format_address <= "01010110"; when "0100100" => format_address <= "01010110"; -- 300 when "0101110" => format_address <= "01011001"; when "0101111" => format_address <= "01010110"; when "0110000" => format_address <= "01010110"; when others => format_address <= "01111111"; end case; when "101101" => -- display reading units below graph if col_address >= "0011001" and col_address <= "0011110" then case volt_units is when "00" => format_address <= "0110" & (col_address(3 downto 0) + "1110"); when "01" => format_address <= "011" & (col_address(4 downto 0) + "10100"); when "10" => format_address <= "01111" & col_address(2 downto 0); when others => format_address <= "01111111"; end case; else format_address <= "01111111"; end if; -- all unclaimed pixels should be white when others => format_address <= "01111111"; end case; -- Okay, now that we've figured out the address of the character in the -- character ROM, pass it to output. rom_address(8 downto 3) <= format_data; end process display_character; end organization;