文章目录
HDLBits刷题笔记
以下是在做HDLBits时的一些刷题笔记,截取一些重要内容并加上自己的理解,方便以后翻阅查找
Circuits
Mux2to1
这个题很简单,记录一下是因为示例给出了多路复用器的真值表解法:
但是下一题就用不了真值表啦,请看示例里的提问:
他问我们为什么这一题用不了真值表解法,原因很简单:我们使用的是位与,b是向量而sel不是,在执行sel&b时b会自动截短,所以sel&b等同于sel&b[0],答案自然不正确。
Mux9to1v
还是两种基本方法,示例则给出了不需要写default的写法(之前有提到过):
注意示例部分提出了使用’1或者’0来给一个向量全部赋值为1或者0,但是注意有的编译器不支持这样的写法。常用赋全1或0的方法如下:
reg [7:0]r_vec;
always@(*)begin
/*赋全1*/
r_vec = 8'hff; //是个verilog编译器就支持
r_vec = '1; //Verilog不一定支持,但SV中一定支持
r_vec = -1;//对于无符号数可以这么写
/*赋全0*/
r_vec = 8'h00; //是个verilog编译器就支持
r_vec = '0; //Verilog不一定支持,但SV中一定支持
r_vec = 0; //对于无符号数和有符号数都可以这么写
end
其实这个知识点别看很简单,但却是挺实用的,所以还是熟悉一下比较好。
Mux256to1
这一题记录一下,也是为了一个小点。之前说过for循环里的索引不能使用复杂变量而只能使用数字或者常数,否则不可综合:
// 静态循环次数,可综合
parameter WIDTH = 16;
for (int i = 0; i < WIDTH; i++) begin
result[i] = data[i] & mask[i];
end
// 动态循环次数,不可综合
input [7:0] count;
for (int i = 0; i < count; i++) begin // 无法综合
sum = sum + data[i];
end
根本原因之前有说过,for循环是给综合器和仿真器看的,它会把循环里的i用数字0、1、2…替换掉。如果索引的边界是动态的,综合器就不会综合了(for循环对于综合器来说就是复制电路结构,动态边界会让综合器不知道复制多少遍),所以循环次数只能是静态的。但是在向量里的动态按位索引(注意这里强调是按位索引而不是部分索引)是可综合的,综合的结果就是一个选择器。正好把本题答案附上:
但是向量里的动态索引并不是随便写都合理,下一题就会看到简单的动态部分索引不可综合,如下图是我一步步做下来的结果:
做这个题的过程中,我先后试了两种方法,分别是向量的部分索引和for循环的循环变量索引,但是都不成功,原因在Hint里面说了:
总结一下,就是说向量确实支持动态索引,但是综合器必须明确知道索引的偏移量(就是向量的上下界之差)是一个定值(例如本题动态索引的偏移量是这个4常数),即索引的起点可以变,但是索引的偏移量不能变。那么综合器怎么知道你的偏移量是不是个常数呢?很简单,Verilog直接给你提供了新的语法,只要不按这种语法动态索引,Verilog编译器就认为你的语法有误:
reg [7:0] r_data;
reg [2:0] r_addr; // 动态索引变量
reg r_offset; //动态偏移变量
/*合法*/
reg r_bit_value = r_data[r_addr] ;//动态位索引
/*不合法*/
reg [1:0] r_bits_value_wrong = r_data[r_addr+1:r_addr];//直接动态部分索引不合法(哪怕偏移量明确是个常数,综合器不会给你算的)
/*合法*/
reg [1:0] r_bits_value_right0 = r_data[r_addr+:1];//偏移动态部分索引合法,从r_addr开始,选择1位(向上)
/*合法*/
reg [1:0] r_bits_value_right0 = r_data[r_addr-:1];//偏移动态部分索引合法,从r_addr开始,选择1位(向下)
/*不合法*/
reg [1:0] r_bits_value_right0 = r_data[r_addr-:r_offset];//偏移动态部分索引合法,从r_addr开始,选择r_offset位(向下)
结论就是想要动态索引向量,只有动态位索引和动态偏移量索引两种方式,并且动态偏移量索引必须保证偏移量是个定值,而且要遵循专门的语法结构。
这个偏移量索引工程上挺常用的,一定要记住!!!
Exams/ece241 2014 q1c
这一题考察有符号数的溢出怎么计算,与其说是个编程题,不如说是个数学题。
首先说一说有符号数和无符号数的区别:有符号数比无符号数多一个符号位,一般符号位取最高位,符号位为1代表是负数,反之为正数。
其次是符号数的拓展(很重要!!),正数在将拓展位全部置零,负数将拓展位全部置为1(很容易忘记)。
其实这里很好理解,如果负数拓展位为0,符号位不久变成0了吗,结果拓展成正数了。
最后是如何判断符号数溢出,主要有两种方法,这里分别做一下笔记:
第一种方式的是根据二进制数的周期性确定的。首先二进数如果你不断加1,就会出现数据循环往复的情况,例如如果向量只有1位,那么0+1=1,随后1+1=0,进而不断循环。而在数学上,有一种东西非常适合描述周期性的东西,那就是圆,我们可以将二进制数据等间隔画在圆上体现其周期性:
如果是符号数,那就显然蓝色部分是负数(注意是补码表示,所以111代表-1、110代表-2…),红色部分是正数(000代表0、001代表1…)。我们以正数相加为例,正数相加,就可以看作从12点方向顺时针旋转一定举例,例如左侧图中说明了001+010的情形,右侧图说明了011+011的情形。那么什么是溢出呢?就是一下子走多了,需要向更高位进位(或者退位),但又不存在最高位。例如右图,直接表现就是两个正数相加本应该是正数,但是却变成了负数,集合上表示就是顺时针走过了超过180度的角度,这就叫溢出;反之对于两个负数相加,就是逆时针转,超过180度也溢出了变成正数。所以其实溢出可以看作是顺(逆)时针走超过180度,那么显然正数和一个负数的和可以看作先顺时针(小于180度)转一定角度,再逆时针转一定角度(小于180度),是一定不会出现一口气顺(逆时针)走超过180度的情况,也就是不会溢出。总结溢出条件就是:1.输入数据同符号;2.输出结果与输入数据符号相反。
第二种方式是进位,需要依据符号拓展的特点,也是用图就很好解释,如下:
显然拓展原来两位的数据空间在符号拓展后就到了三位数据圆的上半部分,那在拓展之后的圆中,如果数据出现在下半部分就是不可能情况,即出现了异常情况溢出。观察一下就可以看到溢出数据特点是拓展符号位和原符号位不同。总结溢出条件就是:计算结果sum的拓展符号位和原符号位不同。
于是,根据这两个基本原理,我们编写出以下两种代码:
这里多记一些符号数的基本原理,符号数的处理比无符号数复杂,理解了就更好记忆了。
Adder100
这一题我本来想用拓展向量进位的方式完成cout的赋值功能,但看见题干说Expected solution length: Around 1 line,就想想该用什么方法一行解决这个问题,最后想到了拼接向量的方式,刚好答案也是这么给的:
Kmap1
从这里开始又是数学题了。卡诺图的基本原理之前说过,这里就不重复记了。但是我发现第一题的化简不是最简,这里给出我的化简思路:
但是我忘记了卡诺图其实时可以重叠的:根本原因是通过合并1去化简卡诺图是基于或运算的,而或运算是允许元素重叠的(True|True=1)。其实,根本上说,卡诺图化简的目标就是最小化项数,最小化项数就是卡诺图的核心优势所在。太久没用离散数学有点忘了,刚好现在拾起来。这里给出最简化简思路:
最后是答案:
其实我的公式可以通过吸收率A | (A & B) = A来通过公式化简为示例的形式。从左到右依次化简即可,很简单,我就不写出过程了。
最后在示例注释里提到了SOP和POS,即Sum of Products积之和 以及 Product of Sums和之积,这是离散公式的两种表达方式,以下是区别:
表示类型 | 基本形式 | 电路实现 |
---|---|---|
SOP | F = A·B + C·D + … | 先用与门,再用或门 |
POS | F = (A+B)·(C+D)·… | 先用或门,再用与门 |
在本例中很特殊,SOP和POS这两种形式完全一样。
这里顺带再记一下如何根据卡诺图分别得到SOP和POS这两种形式。首先SOP的核心是或运算,而或运算是会吸收0的,因为1|0=1,这样就意味着我们要想将卡诺图化简为SOP的形式绝对不能把0引入(不然就会被其他的1项吸收);同时又是因为1|1=1,所以就允许选择1项时卡诺图可以重叠,故总结SOP化简过程如下:
- 寻找"1"的分组: 在卡诺图中寻找所有值为1的单元格;形成尽可能大的矩形分组(大小必须是2的幂:1, 2, 4, 8…)允许分组重叠,追求最少的分组数。
- 确定每个分组的表达式:若变量取值恒为1,则保留该变量;若变量取值恒为0,则保留该变量的反;若变量取值变化,则忽略该变量;将这些变量与起来形成一个子项。
- 形成最终表达式:将每个子项用或运算连接。
随后就是POS方式,与SOP相反,POS化简是要找出所有0项。POS的核心是与运算,首先与运算是会吸收1的,因为1·0 = 0,这样就意味着我们不能把1引入(不然就会被其他1项吸收);同时因为0·0=0,这样就意味着选择0项时卡诺图可以重叠,故总结POS的化简过程如下:
- 寻找"1"的分组: 在卡诺图中寻找所有值为0的单元格;形成尽可能大的矩形分组(大小必须是2的幂:1, 2, 4, 8…)允许分组重叠,追求最少的分组数。
- 定每个分组的表达式:若变量取值恒为0,则保留该变量;若变量取值恒为1,则保留该变量的反;若变量取值变化,则忽略该变量;将这些变量或起来形成一个子项。
- 形成最终表达式:将每个子项用与运算连接。
其实,再说一说SOP和POS的最根本的化简公式,首先是SOP:
(
a
∧
b
)
∨
(
¬
a
∧
b
)
=
(
a
∨
¬
a
)
∧
b
(分配律逆向:提取公因子
b
)
=
True
∧
b
(排中律:
a
∨
¬
a
=
True
)
=
b
(恒等律:
True
∧
A
=
A
)
\begin{align*} (a \land b) \lor (\neg a \land b) &= (a \lor \neg a) \land b \quad && \text{(分配律逆向:提取公因子$b$)} \\ &= \text{True} \land b \quad && \text{(排中律:$a \lor \neg a = \text{True}$)} \\ &= b \quad && \text{(恒等律:$\text{True} \land A = A$)} \end{align*}
(a∧b)∨(¬a∧b)=(a∨¬a)∧b=True∧b=b(分配律逆向:提取公因子b)(排中律:a∨¬a=True)(恒等律:True∧A=A)
其次是POS:
(
a
∨
b
)
∧
(
¬
a
∨
b
)
=
[
a
∧
(
¬
a
∨
b
)
]
∨
[
b
∧
(
¬
a
∨
b
)
]
(分配律)
=
(
a
∧
¬
a
)
∨
(
a
∧
b
)
∨
(
b
∧
¬
a
)
∨
(
b
∧
b
)
(展开)
=
False
∨
(
a
∧
b
)
∨
(
b
∧
¬
a
)
∨
b
(矛盾律和幂等律)
=
(
a
∧
b
)
∨
(
¬
a
∧
b
)
∨
b
(交换律)
=
[
(
a
∨
¬
a
)
∧
b
]
∨
b
(分配律逆向)
=
(
True
∧
b
)
∨
b
(排中律)
=
b
∨
b
(恒等律)
=
b
(幂等律)
\begin{align*} (a \lor b) \land (\neg a \lor b) &= \big[a \land (\neg a \lor b)\big] \lor \big[b \land (\neg a \lor b)\big] \quad && \text{(分配律)} \\ &= (a \land \neg a) \lor (a \land b) \lor (b \land \neg a) \lor (b \land b) \quad && \text{(展开)} \\ &= \text{False} \lor (a \land b) \lor (b \land \neg a) \lor b \quad && \text{(矛盾律和幂等律)} \\ &= (a \land b) \lor (\neg a \land b) \lor b \quad && \text{(交换律)} \\ &= \big[(a \lor \neg a) \land b\big] \lor b \quad && \text{(分配律逆向)} \\ &= (\text{True} \land b) \lor b \quad && \text{(排中律)} \\ &= b \lor b \quad && \text{(恒等律)} \\ &= b \quad && \text{(幂等律)} \end{align*}
(a∨b)∧(¬a∨b)=[a∧(¬a∨b)]∨[b∧(¬a∨b)]=(a∧¬a)∨(a∧b)∨(b∧¬a)∨(b∧b)=False∨(a∧b)∨(b∧¬a)∨b=(a∧b)∨(¬a∧b)∨b=[(a∨¬a)∧b]∨b=(True∧b)∨b=b∨b=b(分配律)(展开)(矛盾律和幂等律)(交换律)(分配律逆向)(排中律)(恒等律)(幂等律)
记住这两个公式其实就明白了为什么卡诺图能如此化简。上篇笔记有一个卡诺图的截图推导就反映了SOP的公式,至于POS就不罗嗦了。
最后,就是怎么选用SOP和POS呢?一般说就是0少用POS,1少用SOP。例如本题,如果直接找0(因为只有一个0),所以就可以一下得到化简结果为a|b|c。
Kmap2
这一题刚好把复习的理论练练手,看到0比较少,我们优先选用POS形式:
其次是SOP形式:
所以得到这一题的答案:
Kmap3
这一题的卡诺图稍微变了一下,但不要怕,直接将对应的d的值改一下就好,改成01之后就是熟悉的卡诺图了,并附上我的划分方式:
这里顺带附上我的答案:
Kmap4
这一题的特点是01分布很有规律性,但就是无法用卡诺图化简,只能用观察法得到简要答案,这里我就提供三种解法:
Exams/ece241 2013 q2
这一题需要先画出卡诺图:
其中X表示这个系统不会出现,在画卡诺图时可以认为是0也可以认为是1,总之怎么画矩形大怎么选择X位,以下是我的划分:
下面是我的答案:
Exams/ece241 2014 q3
这一题需要好好审审题干,他的核心思想是要求你使用选择器来实现卡诺图,如果我们需要完成一个完整的系统就需要这么写:
always@(*)begin
case({a,b,c,d})
4'b0000:f = 0;
4'b0001:f = 0;
... ...
default:f = 0;
endcase
end
但是这一题强行给你拆成了两部分,如下:
我们假设ab已经是00了,也就是mux_in[0]被激活,此时mux_in[0]应该怎么在top里被赋值?显然就是看卡诺图里ab = 00那一列,根据cd的赋值情况给mux_in[0]赋值,这也适合使用case语句进行实现。那么其他路的mux_in怎么赋值呢?我们发现这四路没有什么特别的逻辑关系,所以就选择独立赋值,于是得到如下答案:
这一题示例使用assign会简洁得多。但其实我这样写也是有问题的,那就是题目中明确说明只能使用2选1选择器,而MUX有是4位,那会被RTL分析成如下的四选一选择器:
不但如此,选择器的输入端还是个向量,这就变成了一个总线类型的4选1选择器,是完全不符合题意的,所以标准答案只能按示例中描述去写!!!
未完待续
本篇文章1截止到Karnaugh Map to Circuit部分。