Know this topic? Give it a go. The lesson is here if you need it.
Two's Complement left a promise hanging: the pattern 1011 reads as 11 unsigned or as −5 signed. The choice of reading "lives in your head and, later, in the types you give your signals." Later is now.
A std_logic_vector is deliberately meaning-free: wires, in order, nothing more. The moment a design treats a bundle as a number, you declare its reading explicitly with a type.
VHDL keeps its number-reading vector types in a second ieee package, numeric_std. Anatomy of a Component promised you'd add this line when the number types appeared. Add it now:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all; -- unsigned and signed live here
The declarations look exactly like vectors because they are vectors (same wires, same indexing and slices), plus a declared reading:
signal level_i : unsigned(6 downto 0); -- 7 wires read as 0 to 127
signal temp_i : signed(7 downto 0); -- 8 wires read as -128 to +127
VHDL has three distinct types, and the distinction matters. std_logic_vector says "no numeric meaning"; unsigned and signed each fix one reading, and the tools refuse numeric operations on the meaning-free one.
Not every number in a design rides on wires. The limits you compare against, the widths in declarations, the values a testbench feeds in: those are numbers in the source text, with no bundle behind them.
VHDL's type for them is integer, with two built-in subtypes that document intent: natural (0 and up) and positive (1 and up). Declaring a limit as natural is a statement to the reader and the compiler; a negative value in that spot becomes an error.
Can a signal be an integer? It's legal, and code that does it constrains the range (signal count : natural range 0 to 100) so the synthesizer knows how many wires to spend. You'll meet that form in Counting to N. For ports and datapaths, prefer unsigned/signed, where you chose the exact wire count yourself.
A limit that appears as a bare number is a question every reader has to answer again ("why 80?"). Name it once, next to your signal declarations:
constant LOW_MARK : natural := 20;
constant HIGH_MARK : natural := 80;
Constants are written ALL_CAPS by convention. A constant has no wires and no driver: at synthesis it dissolves into the logic that uses it. That gives "change the value" a hardware meaning: edit one line and a different circuit gets built.
A constant is private to the file that declares it. Its overridable cousin, the generic, can be set from outside per instance; generics get their own treatment in Hierarchy and Reuse.
The payoff of a declared reading: the tools can now compare. Comparison operators get their formal lesson in Arithmetic and Relational Operators, but you need their everyday shape now. Like the tri-state recipe in Port Directions, take this one as-is:
low_o <= '1' when level_i < LOW_MARK else '0';
A comparison produces a true/false answer, not a wire level, so the when/else wrapper (the same shape as the tri-state recipe) turns "true" into '1'. The mechanism behind it arrives in Conditional Assignment.
The comparison reads the signal against the constant under the declared reading. < is strict: a level of 19 trips the low flag, exactly 20 does not.
CAUTION
Common Mistake: comparing a meaning-free vector. std_logic_vector has no numeric reading, and comparing one against a number refuses to compile; declare the signal unsigned or signed instead.
The example panel on the right (Tank Gauge) is the level monitor this lesson has been building. A 7-wire unsigned fill level is checked against LOW_MARK (20) and HIGH_MARK (80). Step through the waveform. At 19 the low flag is up. At exactly 20, the mark itself, both flags are calm, because the comparisons are strict. high_o first rises at 81, one past the mark. One thing you will not find in the waveform is LOW_MARK itself: a constant is not a wire. It dissolved into the comparator at build time.
unsigned and signed, from numeric_std, declare how a vector is read as a number.integer and its subtypes natural and positive are wire-less numbers: the types for limits, literals, and counts in the source text.ALL_CAPS constant: one definition, and at synthesis it dissolves into the circuit that uses it.