Skip to content

Commit 7a5b7b6

Browse files
committed
- Miscommit, moved Multiplxers and Latches to lessons folder.
1 parent 73edce9 commit 7a5b7b6

File tree

1 file changed

+241
-0
lines changed

1 file changed

+241
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
# Multiplexers and Latches
2+
## The Humble Multiplexer
3+
The 2-input multiplexer (or mux for short) is one of the basic building blocks of RTL design. It
4+
takes 2 n-bit inputs (we will call them a and b) and a select bit (sel) and has a single n-bit
5+
output (out). If sel = 0, out is set to a, otherwise it is set to b.
6+
7+
There are several ways to implement a mux. A common choice is the humble if-else statement.
8+
9+
SystemVerilog:
10+
```verilog
11+
logic [n-1:0] a;
12+
logic [n-1:0] b;
13+
logic sel;
14+
logic [n-1:0] out;
15+
16+
always_comb
17+
if sel == 0 begin
18+
out <= a;
19+
end else begin
20+
out <= b;
21+
end
22+
```
23+
24+
VHDL
25+
```vhdl
26+
signal a : std_logic_vector(n-1 downto 0);
27+
signal b : std_logic_vector(n-1 downto 0);
28+
signal sel : std_logic;
29+
signal out : std_logic_vector(n-1 downto 0);
30+
31+
process (a, b, sel) begin
32+
if !sel then
33+
out <= a;
34+
else
35+
out <= b;
36+
end if;
37+
end process;
38+
```
39+
40+
MyHDL
41+
```python
42+
def mux(a, b, sel, out):
43+
@always_comb
44+
def logic():
45+
if not sel:
46+
out.next = a
47+
else:
48+
out.next = b
49+
return logic
50+
```
51+
52+
## Latches
53+
You can also have m-input n-bit muxes. Here's what this looks like for m = 3.
54+
55+
SystemVerilog:
56+
```verilog
57+
logic [n-1:0] a;
58+
logic [n-1:0] b;
59+
logic [n-1:0] c;
60+
logic [1:0] sel;
61+
logic [n-1:0] out;
62+
63+
always_comb
64+
if sel == 0 begin
65+
out <= a;
66+
end else if sel == 1 begin
67+
out <= b;
68+
end else if sel == 2 begin
69+
out <= c;
70+
end
71+
```
72+
73+
VHDL
74+
```vhdl
75+
signal a : std_logic_vector(n-1 downto 0);
76+
signal b : std_logic_vector(n-1 downto 0);
77+
signal c : std_logic_vector(n-1 downto 0);
78+
signal sel : std_logic_vector(1 downto 0);
79+
signal out : std_logic_vector(n-1 downto 0);
80+
81+
process (a, b, c, sel) begin
82+
if sel = b"00" then
83+
out <= a;
84+
elsif sel = b"01" then
85+
out <= b;
86+
elsif sel = b"10" then
87+
out <= c;
88+
end if;
89+
end process;
90+
```
91+
92+
MyHDL
93+
```python
94+
def mux(a, b, c, sel, out):
95+
@always_comb
96+
def logic():
97+
if sel == 0:
98+
out.next = a
99+
elif sel == 1:
100+
out.next = b
101+
elif sel == 2:
102+
out.next = c
103+
return logic
104+
```
105+
106+
Some of you may have noticed an issue with the implementations above: we've inferred a latch! How?
107+
Well let us look step through the cases one at a time:
108+
* If sel = 0, then out is set to a.
109+
* If sel = 1, then out is set to b.
110+
* If sel = 2, then out is set to c.
111+
* If sel = 3, then out is set to ???.
112+
113+
Wait a minute, what should happen to out when sel = 3? Some of you are probably thinking "obviously
114+
it keeps its previous value." And you would be mostly correct. It will keep the previous value that
115+
it was assigned to. However, remember that "keeping its previous value" implies that out is
116+
stateful. The only way in which this is possible is to have a memory element, in this case a latch,
117+
which holds the previous value of out. Since out implementation is now has a memory element in it,
118+
it is no longer combinational (remember, combinational logic cannot contain memory elements). In
119+
fact, if you tried to compile the SystemVerilog example, you would find that it would give you an
120+
error saying that the implementation is not combinational (the others would only give warnings that
121+
a latch was inferred). It is also worth noting that we should always avoid latches, especially in
122+
FPGA implementations, because their behavior tends to be unpredictable and because FPGAs do not
123+
contain latches as native memory elements.
124+
125+
## Where did we go wrong?
126+
When we wrote out 3-input mux, our if statements were not "assignment complete." That is, out is
127+
not assigned for all possible inputs, in this case when sel = 3. If the 3-input mux case, this is
128+
simple to see since which value out is assigned is dependent only on sel. But at some point we may
129+
have a case like this:
130+
131+
SystemVerilog:
132+
```verilog
133+
logic [n-1:0] a;
134+
logic [n-1:0] b;
135+
logic sel0;
136+
logic sel1;
137+
logic [n-1:0] out;
138+
139+
always_comb
140+
if sel0 begin
141+
out <= a;
142+
end else if sel1 begin
143+
out <= b;
144+
end
145+
```
146+
147+
VHDL
148+
```vhdl
149+
signal a : std_logic_vector(n-1 downto 0);
150+
signal b : std_logic_vector(n-1 downto 0);
151+
signal sel0 : std_logic;
152+
signal sel1 : std_logic;
153+
signal out : std_logic_vector(n-1 downto 0);
154+
155+
process (a, b, sel0, sel1) begin
156+
if sel0 = '1' then
157+
out <= a;
158+
elsif sel1 = '1' then
159+
out <= b;
160+
end if;
161+
end process;
162+
```
163+
164+
MyHDL
165+
```python
166+
def foo(a, b, sel0, sel1, out):
167+
@always_comb
168+
def logic():
169+
if sel0:
170+
out.next = a
171+
elif sel1:
172+
out.next = b
173+
return logic
174+
```
175+
176+
This is still a fairly elementary example, but we see that we can have if statements based on
177+
independent inputs which can add even more confusion to the mix.
178+
179+
## How do we prevent this?
180+
The easiest way to make sure that we do not have "assignment incomplete" if statement is to *always*
181+
include and else case in which we assign outputs with default values. In the case of the 3-input
182+
mux, we can simply map both sel = 2 and sel = 3 to input c.
183+
184+
SystemVerilog:
185+
```verilog
186+
logic [n-1:0] a;
187+
logic [n-1:0] b;
188+
logic [n-1:0] c;
189+
logic [1:0] sel;
190+
logic [n-1:0] out;
191+
192+
always_comb
193+
if sel == 0 begin
194+
out <= a;
195+
end else if sel == 1 begin
196+
out <= b;
197+
end else begin
198+
out <= c;
199+
end
200+
```
201+
202+
VHDL
203+
```vhdl
204+
signal a : std_logic_vector(n-1 downto 0);
205+
signal b : std_logic_vector(n-1 downto 0);
206+
signal c : std_logic_vector(n-1 downto 0);
207+
signal sel : std_logic_vector(1 downto 0);
208+
signal out : std_logic_vector(n-1 downto 0);
209+
210+
process (a, b, c, sel) begin
211+
if sel = b"00" then
212+
out <= a;
213+
elsif sel = b"01" then
214+
out <= b;
215+
else then
216+
out <= c;
217+
end if;
218+
end process;
219+
```
220+
221+
MyHDL
222+
```python
223+
def mux(a, b, c, sel, out):
224+
@always_comb
225+
def logic():
226+
if sel == 0:
227+
out.next = a
228+
elif sel == 1:
229+
out.next = b
230+
else:
231+
out.next = c
232+
return logic
233+
```
234+
235+
Note that similar issues can arise when using case statements, so make sure to always include a
236+
default case when using them.
237+
238+
## Summary
239+
* Assignment incomplete if statements lead to inferred latches. This can happen for any conditional
240+
structure,including case statements.
241+
* Always include an else or default case in which you assign default values for all outputs.

0 commit comments

Comments
 (0)